diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-02 18:45:30 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | f914ff8376dd91c4f6f8ca149e1cb6becb622d88 (patch) | |
| tree | 777dc0268eba86721878a715c68f0f09bedb4b18 | |
| parent | b52b8183ce299b5a2d3c3b2f4f8cb94bb443d746 (diff) | |
refactor(components): rewrite Link component
* rename `external` prop to `isExternal`
* rename `download` prop to `isDownload`
* rewrite CSS to reduce code length and complexity
* move link styles in Sass placeholders to avoid repeats because of WordPress
articles
* move NavLink component to molecules
34 files changed, 692 insertions, 615 deletions
diff --git a/.cspell/french-contents.txt b/.cspell/french-contents.txt index 1931b21..2d25575 100644 --- a/.cspell/french-contents.txt +++ b/.cspell/french-contents.txt @@ -1,6 +1,10 @@ Adwords Auxseo +Coreutils +dotfiles +dotig GIDO +htdocs Jasson Londe Massena @@ -12,3 +16,4 @@ référenceur Superdoc Symfony webmarketing +webroot diff --git a/.cspell/project-words.txt b/.cspell/project-words.txt index c9d6014..e2b983c 100644 --- a/.cspell/project-words.txt +++ b/.cspell/project-words.txt @@ -4,8 +4,10 @@ autodocs changefreq coldark commitlint +creativecommons Eckles formatjs +gitlab Gitlab Jamstack LINKEDIN diff --git a/src/components/atoms/links/index.ts b/src/components/atoms/links/index.ts index ad8824e..619f05c 100644 --- a/src/components/atoms/links/index.ts +++ b/src/components/atoms/links/index.ts @@ -1,4 +1,3 @@ export * from './link'; -export * from './nav-link'; export * from './sharing-link'; export * from './social-link'; diff --git a/src/components/atoms/links/link.module.scss b/src/components/atoms/links/link.module.scss deleted file mode 100644 index 4980a1c..0000000 --- a/src/components/atoms/links/link.module.scss +++ /dev/null @@ -1,220 +0,0 @@ -@use "../../../styles/abstracts/functions" as fun; -@use "../../../styles/abstracts/variables" as var; - -/* stylelint-disable no-descending-specificity */ -.link { - &[hreflang] { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]"; - font-size: var(--font-size-sm); - } - } - - &--download { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - } - } - - &--external { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } - } - - &--external#{&}--download { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } - } -} - -:global([data-theme="dark"]) { - :local { - .link { - &--download { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - } - } - - &--external { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } - } - - &--external.link--download { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } - } - } - } -} -/* stylelint-enable no-descending-specificity */ diff --git a/src/components/atoms/links/link.test.tsx b/src/components/atoms/links/link.test.tsx deleted file mode 100644 index 9829c1b..0000000 --- a/src/components/atoms/links/link.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { Link } from './link'; - -describe('Link', () => { - it('render a link', () => { - render(<Link href="#">A link</Link>); - expect(screen.getByRole('link')).toHaveTextContent('A link'); - }); -}); diff --git a/src/components/atoms/links/link.tsx b/src/components/atoms/links/link.tsx deleted file mode 100644 index 1765bb5..0000000 --- a/src/components/atoms/links/link.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import NextLink from 'next/link'; -import { AnchorHTMLAttributes, FC, ReactNode } from 'react'; -import styles from './link.module.scss'; - -export type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & { - /** - * The link body. - */ - children: ReactNode; - /** - * True if it is a download link. Default: false. - */ - download?: boolean; - /** - * True if it is an external link. Default: false. - */ - external?: boolean; - /** - * The link target. - */ - href: string; - /** - * The link target code language. - */ - lang?: string; -}; - -/** - * Link Component - * - * Render a link. - */ -export const Link: FC<LinkProps> = ({ - children, - className = '', - download = false, - external = false, - href, - lang, - ...props -}) => { - const downloadClass = download ? styles['link--download'] : ''; - const linkClass = `${styles.link} ${downloadClass} ${className}`; - const externalLinkClass = `${linkClass} ${styles['link--external']}`; - - return external ? ( - <a {...props} className={externalLinkClass} href={href} hrefLang={lang}> - {children} - </a> - ) : ( - <NextLink {...props} className={linkClass} href={href} hrefLang={lang}> - {children} - </NextLink> - ); -}; diff --git a/src/components/atoms/links/link/index.ts b/src/components/atoms/links/link/index.ts new file mode 100644 index 0000000..e33728e --- /dev/null +++ b/src/components/atoms/links/link/index.ts @@ -0,0 +1 @@ +export * from './link'; diff --git a/src/components/atoms/links/link/link.module.scss b/src/components/atoms/links/link/link.module.scss new file mode 100644 index 0000000..8f94a54 --- /dev/null +++ b/src/components/atoms/links/link/link.module.scss @@ -0,0 +1,70 @@ +@use "../../../../styles/abstracts/placeholders"; + +.link { + color: var(--color-primary); + + &--regular { + @extend %link; + } + + &[hreflang], + &--download, + &--external { + @extend %link-with-icon; + } + + &[hreflang]:not(#{&}--download):not(#{&}--external) { + --is-icon-hidden: ""; + } + + &[hreflang] { + @extend %link-with-lang; + } + + &--download { + @extend %download-link; + } + + &--external { + @extend %external-link; + } + + &--download, + &--external { + &:not([hreflang]) { + --is-lang-hidden: ""; + } + } + + &--external#{&}--download { + @extend %external-download-link; + } +} + +:global([data-theme="light"]) { + :local { + .link { + &--download { + @extend %light-download-link; + } + + &--external { + @extend %light-external-link; + } + } + } +} + +:global([data-theme="dark"]) { + :local { + .link { + &--download { + @extend %dark-download-link; + } + + &--external { + @extend %dark-external-link; + } + } + } +} diff --git a/src/components/atoms/links/link.stories.tsx b/src/components/atoms/links/link/link.stories.tsx index 8351de7..0a6a6c5 100644 --- a/src/components/atoms/links/link.stories.tsx +++ b/src/components/atoms/links/link/link.stories.tsx @@ -1,11 +1,11 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { Link } from './link'; /** * Link - Storybook Meta */ export default { - title: 'Atoms/Typography/Links', + title: 'Atoms/Links/Link', component: Link, argTypes: { children: { @@ -31,7 +31,17 @@ export default { required: false, }, }, - download: { + href: { + control: { + type: 'text', + }, + description: 'The link target.', + type: { + name: 'string', + required: true, + }, + }, + isDownload: { control: { type: 'boolean', }, @@ -45,7 +55,7 @@ export default { required: false, }, }, - external: { + isExternal: { control: { type: 'boolean', }, @@ -59,16 +69,6 @@ export default { required: false, }, }, - href: { - control: { - type: 'text', - }, - description: 'The link target.', - type: { - name: 'string', - required: true, - }, - }, lang: { control: { type: 'text', @@ -94,64 +94,62 @@ export const Default = Template.bind({}); Default.args = { children: 'A link', href: '#', - download: false, - external: false, }; /** * Links Stories - Download */ -export const Download = Template.bind({}); -Download.args = { +export const DownloadLink = Template.bind({}); +DownloadLink.args = { children: 'A link to a file', href: '#', - download: true, - external: false, + isDownload: true, + isExternal: false, }; /** - * Links Stories - DownloadWithLang + * Links Stories - Download link with lang */ -export const DownloadWithLang = Template.bind({}); -DownloadWithLang.args = { +export const DownloadLinkWithLang = Template.bind({}); +DownloadLinkWithLang.args = { children: 'A link to a file', href: '#', - download: true, - external: false, + isDownload: true, + isExternal: false, lang: 'en', }; /** * Links Stories - External */ -export const External = Template.bind({}); -External.args = { +export const ExternalLink = Template.bind({}); +ExternalLink.args = { children: 'A link', href: '#', - download: false, - external: true, + isDownload: false, + isExternal: true, }; /** - * Links Stories - External download + * Links Stories - External download link */ export const ExternalDownload = Template.bind({}); ExternalDownload.args = { children: 'A link', href: '#', - download: true, - external: true, + isDownload: true, + isExternal: true, }; /** - * Links Stories - External With Lang + * Links Stories - External link with Lang */ -export const ExternalWithLang = Template.bind({}); -ExternalWithLang.args = { +export const ExternalLinkWithLang = Template.bind({}); +ExternalLinkWithLang.args = { children: 'A link', href: '#', - download: false, - external: true, + isDownload: false, + isExternal: true, lang: 'en', }; @@ -162,19 +160,19 @@ export const ExternalDownloadWithLang = Template.bind({}); ExternalDownloadWithLang.args = { children: 'A link', href: '#', - download: true, - external: true, + isDownload: true, + isExternal: true, lang: 'en', }; /** * Links Stories - With Lang */ -export const WithLang = Template.bind({}); -WithLang.args = { +export const LinkLang = Template.bind({}); +LinkLang.args = { children: 'A link', href: '#', - download: false, - external: false, + isDownload: false, + isExternal: false, lang: 'en', }; diff --git a/src/components/atoms/links/link/link.test.tsx b/src/components/atoms/links/link/link.test.tsx new file mode 100644 index 0000000..ad1951d --- /dev/null +++ b/src/components/atoms/links/link/link.test.tsx @@ -0,0 +1,47 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { Link } from './link'; + +describe('Link', () => { + it('renders a link', () => { + const anchor = 'porro'; + const target = '/tempora'; + + render(<Link href={target}>{anchor}</Link>); + + expect(rtlScreen.getByRole('link', { name: anchor })).toHaveAttribute( + 'href', + target + ); + }); + + it('can render an external link', () => { + const anchor = 'accusamus'; + const target = 'https://www.example.com'; + + render( + <Link href={target} isExternal> + {anchor} + </Link> + ); + + expect(rtlScreen.getByRole('link', { name: anchor })).toHaveClass( + 'link--external' + ); + }); + + it('can render a download link', () => { + const anchor = 'dolor'; + const target = '/officiis.pdf'; + + render( + <Link href={target} isDownload> + {anchor} + </Link> + ); + + expect(rtlScreen.getByRole('link', { name: anchor })).toHaveClass( + 'link--download' + ); + }); +}); diff --git a/src/components/atoms/links/link/link.tsx b/src/components/atoms/links/link/link.tsx new file mode 100644 index 0000000..e88cc7c --- /dev/null +++ b/src/components/atoms/links/link/link.tsx @@ -0,0 +1,90 @@ +import NextLink from 'next/link'; +import { + forwardRef, + type AnchorHTMLAttributes, + type ForwardRefRenderFunction, + type ReactNode, +} from 'react'; +import styles from './link.module.scss'; + +export type LinkProps = Omit< + AnchorHTMLAttributes<HTMLAnchorElement>, + 'children' | 'download' | 'hrefLang' | 'lang' +> & { + /** + * The link body. + */ + children: ReactNode; + /** + * Should we disable the default transition on links? + * + * @default false + */ + disableTransition?: boolean; + /** + * True if it is a download link. + * + * @default false + */ + isDownload?: boolean; + /** + * True if it is an external link. + * + * @default false + */ + isExternal?: boolean; + /** + * The link target. + */ + href: string; + /** + * The link target code language. + */ + lang?: string; +}; + +const LinkWithRef: ForwardRefRenderFunction<HTMLAnchorElement, LinkProps> = ( + { + children, + className = '', + disableTransition = false, + isDownload = false, + isExternal = false, + href, + lang, + rel = '', + ...props + }, + ref +) => { + const LinkComponent = isExternal ? 'a' : NextLink; + const linkClass = [ + styles.link, + styles[disableTransition ? '' : 'link--regular'], + styles[isDownload ? 'link--download' : ''], + styles[isExternal ? 'link--external' : ''], + className, + ].join(' '); + const linkRel = + isExternal && !rel.includes('external') ? `external ${rel}` : rel; + + return ( + <LinkComponent + {...props} + className={linkClass} + href={href} + hrefLang={lang} + ref={ref} + rel={linkRel} + > + {children} + </LinkComponent> + ); +}; + +/** + * Link Component + * + * Render a link. + */ +export const Link = forwardRef(LinkWithRef); diff --git a/src/components/atoms/links/nav-link.test.tsx b/src/components/atoms/links/nav-link.test.tsx deleted file mode 100644 index acf3225..0000000 --- a/src/components/atoms/links/nav-link.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { NavLink } from './nav-link'; - -describe('NavLink', () => { - it('renders a nav link to blog page', () => { - render(<NavLink href="/blog" label="Blog" />); - expect(screen.getByRole('link', { name: 'Blog' })).toHaveAttribute( - 'href', - '/blog' - ); - }); -}); diff --git a/src/components/atoms/links/nav-link.tsx b/src/components/atoms/links/nav-link.tsx deleted file mode 100644 index 109529c..0000000 --- a/src/components/atoms/links/nav-link.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import Link from 'next/link'; -import { FC, ReactNode } from 'react'; -import styles from './nav-link.module.scss'; - -export type NavLinkProps = { - /** - * Link target. - */ - href: string; - /** - * Link label. - */ - label: string; - /** - * Link logo. - */ - logo?: ReactNode; -}; - -/** - * NavLink component - * - * Render a navigation link. - */ -export const NavLink: FC<NavLinkProps> = ({ href, label, logo }) => { - return ( - <Link className={styles.link} href={href}> - {logo} - {label} - </Link> - ); -}; diff --git a/src/components/molecules/layout/branding.tsx b/src/components/molecules/layout/branding.tsx index 981da74..dceee5e 100644 --- a/src/components/molecules/layout/branding.tsx +++ b/src/components/molecules/layout/branding.tsx @@ -1,8 +1,7 @@ -import Link from 'next/link'; import { type FC, useRef } from 'react'; import { useIntl } from 'react-intl'; import { useStyles } from '../../../utils/hooks'; -import { Heading } from '../../atoms'; +import { Heading, Link } from '../../atoms'; import { FlippingLogo, type FlippingLogoProps } from '../images'; import styles from './branding.module.scss'; diff --git a/src/components/molecules/layout/meta.tsx b/src/components/molecules/layout/meta.tsx index 094c420..63909a4 100644 --- a/src/components/molecules/layout/meta.tsx +++ b/src/components/molecules/layout/meta.tsx @@ -336,7 +336,7 @@ export const Meta: FC<MetaProps> = ({ ); case 'website': return typeof value === 'string' ? ( - <Link href={value} external={true}> + <Link href={value} isExternal> {value} </Link> ) : null; diff --git a/src/components/molecules/nav/index.ts b/src/components/molecules/nav/index.ts index 9c46050..fe7cd0b 100644 --- a/src/components/molecules/nav/index.ts +++ b/src/components/molecules/nav/index.ts @@ -1,3 +1,4 @@ export * from './breadcrumb'; +export * from './nav-link'; export * from './nav-list'; export * from './pagination'; diff --git a/src/components/molecules/nav/nav-link/index.ts b/src/components/molecules/nav/nav-link/index.ts new file mode 100644 index 0000000..f1b68c2 --- /dev/null +++ b/src/components/molecules/nav/nav-link/index.ts @@ -0,0 +1 @@ +export * from './nav-link'; diff --git a/src/components/atoms/links/nav-link.module.scss b/src/components/molecules/nav/nav-link/nav-link.module.scss index e72885a..8a7d371 100644 --- a/src/components/atoms/links/nav-link.module.scss +++ b/src/components/molecules/nav/nav-link/nav-link.module.scss @@ -1,29 +1,39 @@ -@use "../../../styles/abstracts/functions" as fun; -@use "../../../styles/abstracts/mixins" as mix; -@use "../../../styles/abstracts/placeholders"; +@use "../../../../styles/abstracts/functions" as fun; +@use "../../../../styles/abstracts/placeholders"; .link { --draw-border-thickness: #{fun.convert-px(4)}; --draw-border-color1: var(--color-primary-light); --draw-border-color2: var(--color-primary-lighter); - display: inline-flex; - flex-flow: column nowrap; + display: flex; + flex-flow: row wrap; place-items: center; place-content: center; row-gap: var(--spacing-2xs); - min-width: var(--link-min-width, fun.convert-px(85)); - padding: var(--spacing-xs); - background: inherit; + min-width: var(--link-min-width, fun.convert-px(80)); + padding: var(--spacing-xs) var(--spacing-xs) var(--spacing-2xs); + background: none; + border-radius: 8%; font-size: var(--font-size-sm); font-variant: small-caps; font-weight: 600; - line-height: 1; + text-align: center; text-decoration: none; - @include mix.media("screen") { - @include mix.dimensions("md") { - border-radius: 8%; + &--inline { + width: fit-content; + + .logo { + margin-right: var(--spacing-xs); + } + } + + &--stack { + .logo { + flex: 0 0 100%; + display: flex; + place-content: center; } } @@ -32,6 +42,10 @@ @extend %draw-borders; } + &:hover { + color: var(--color-primary-light); + } + &:focus { color: var(--color-primary-light); } @@ -40,6 +54,8 @@ --draw-border-color1: var(--color-primary-dark); --draw-border-color2: var(--color-primary-light); + color: var(--color-primary-dark); + @extend %draw-borders; } } diff --git a/src/components/atoms/links/nav-link.stories.tsx b/src/components/molecules/nav/nav-link/nav-link.stories.tsx index 7fca926..4e8400f 100644 --- a/src/components/atoms/links/nav-link.stories.tsx +++ b/src/components/molecules/nav/nav-link/nav-link.stories.tsx @@ -1,11 +1,12 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Icon } from '../../../atoms'; import { NavLink as NavLinkComponent } from './nav-link'; /** * NavLink - Storybook Meta */ export default { - title: 'Atoms/Typography/Links', + title: 'Molecules/Nav/NavLink', component: NavLinkComponent, argTypes: { href: { @@ -42,14 +43,37 @@ export default { } as ComponentMeta<typeof NavLinkComponent>; const Template: ComponentStory<typeof NavLinkComponent> = (args) => ( - <NavLinkComponent {...args} /> + <div style={{ width: 'fit-content' }}> + <NavLinkComponent {...args} /> + </div> ); /** - * Links Stories - Nav Link + * NavLink Stories - Default */ -export const NavLink = Template.bind({}); -NavLink.args = { +export const Default = Template.bind({}); +Default.args = { href: '#', label: 'A nav link', }; + +/** + * NavLink Stories - StackWithLogo + */ +export const StackWithLogo = Template.bind({}); +StackWithLogo.args = { + href: '#example', + label: 'A nav link', + logo: <Icon aria-hidden shape="home" />, +}; + +/** + * NavLink Stories - InlineWithLogo + */ +export const InlineWithLogo = Template.bind({}); +InlineWithLogo.args = { + href: '#example', + isInline: true, + label: 'A nav link', + logo: <Icon aria-hidden shape="home" />, +}; diff --git a/src/components/molecules/nav/nav-link/nav-link.test.tsx b/src/components/molecules/nav/nav-link/nav-link.test.tsx new file mode 100644 index 0000000..aa9b557 --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.test.tsx @@ -0,0 +1,28 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { NavLink } from './nav-link'; + +describe('NavLink', () => { + it('renders a link', () => { + const label = 'eius'; + const target = '#harum'; + + render(<NavLink href={target} label={label} />); + + expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute( + 'href', + target + ); + }); + + it('can render a nav link with inlined contents', () => { + const label = 'eius'; + const target = '#harum'; + + render(<NavLink href={target} isInline label={label} />); + + expect(rtlScreen.getByRole('link', { name: label })).toHaveClass( + 'link--inline' + ); + }); +}); diff --git a/src/components/molecules/nav/nav-link/nav-link.tsx b/src/components/molecules/nav/nav-link/nav-link.tsx new file mode 100644 index 0000000..f9fc529 --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.tsx @@ -0,0 +1,44 @@ +import { + type ForwardRefRenderFunction, + forwardRef, + type ReactNode, +} from 'react'; +import { Link, type LinkProps } from '../../../atoms'; +import styles from './nav-link.module.scss'; + +export type NavLinkProps = Omit<LinkProps, 'children' | 'disableTransition'> & { + /** + * Should the logo and label be inlined? + * + * @default false + */ + isInline?: boolean; + /** + * The link label. + */ + label: string; + /** + * The link logo. + */ + logo?: ReactNode; +}; + +const NavLinkWithRef: ForwardRefRenderFunction< + HTMLAnchorElement, + NavLinkProps +> = ({ className = '', isInline = false, label, logo, ...props }, ref) => { + const linkClass = [ + styles.link, + styles[isInline ? 'link--inline' : 'link--stack'], + className, + ].join(' '); + + return ( + <Link {...props} className={linkClass} disableTransition ref={ref}> + {logo ? <span className={styles.logo}>{logo}</span> : null} + {label} + </Link> + ); +}; + +export const NavLink = forwardRef(NavLinkWithRef); diff --git a/src/components/molecules/nav/nav-list.module.scss b/src/components/molecules/nav/nav-list.module.scss index ff99581..316638e 100644 --- a/src/components/molecules/nav/nav-list.module.scss +++ b/src/components/molecules/nav/nav-list.module.scss @@ -1,4 +1,14 @@ .nav { + &--main { + width: fit-content; + } + + &--main & { + &__item { + flex: 1; + } + } + &--footer & { &__item:not(:first-child) { &::before { diff --git a/src/components/molecules/nav/nav-list.tsx b/src/components/molecules/nav/nav-list.tsx index 55c2aa9..b3c7138 100644 --- a/src/components/molecules/nav/nav-list.tsx +++ b/src/components/molecules/nav/nav-list.tsx @@ -1,5 +1,6 @@ import type { FC, ReactNode } from 'react'; -import { Link, List, ListItem, Nav, NavLink, type NavProps } from '../../atoms'; +import { Link, List, ListItem, Nav, type NavProps } from '../../atoms'; +import { NavLink } from './nav-link'; import styles from './nav-list.module.scss'; export type NavItem = { @@ -49,7 +50,7 @@ export const NavList: FC<NavListProps> = ({ ...props }) => { const kindClass = `nav--${kind}`; - const navClass = `${styles[kindClass]} ${className}`; + const navClass = `${styles.nav} ${styles[kindClass]} ${className}`; /** * Get the nav items. diff --git a/src/components/organisms/layout/comment.module.scss b/src/components/organisms/layout/comment.module.scss index f645354..bf8aada 100644 --- a/src/components/organisms/layout/comment.module.scss +++ b/src/components/organisms/layout/comment.module.scss @@ -1,11 +1,13 @@ @use "../../../styles/abstracts/functions" as fun; @use "../../../styles/abstracts/mixins" as mix; +@use "../../../styles/abstracts/placeholders"; .wrapper { padding: var(--spacing-md); background: var(--color-bg); border: fun.convert-px(1) solid var(--color-border); - box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light), + box-shadow: + fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light), fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2) var(--color-shadow); @@ -50,7 +52,8 @@ height: fun.convert-px(85); position: relative; border-radius: fun.convert-px(3); - box-shadow: 0 0 0 fun.convert-px(1) var(--color-shadow-light), + box-shadow: + 0 0 0 fun.convert-px(1) var(--color-shadow-light), fun.convert-px(2) fun.convert-px(2) 0 fun.convert-px(1) var(--color-shadow); @@ -80,6 +83,45 @@ .body { overflow-wrap: break-word; + + :global { + a { + @extend %link; + + &[hreflang], + &.download, + &.external { + @extend %link-with-icon; + } + + &[hreflang] { + @extend %link-with-lang; + } + + &[hreflang]:not(.download, .external) { + --is-icon-hidden: ""; + } + + &.download { + @extend %download-link; + } + + &.external { + @extend %external-link; + } + + &.download, + &.external { + &:not([hreflang]) { + --is-lang-hidden: ""; + } + } + + &.external.download { + @extend %external-download-link; + } + } + } } .footer { diff --git a/src/components/organisms/layout/summary.module.scss b/src/components/organisms/layout/summary.module.scss index 5052f73..b6cb4f4 100644 --- a/src/components/organisms/layout/summary.module.scss +++ b/src/components/organisms/layout/summary.module.scss @@ -1,5 +1,6 @@ @use "../../../styles/abstracts/functions" as fun; @use "../../../styles/abstracts/mixins" as mix; +@use "../../../styles/abstracts/placeholders"; .wrapper { display: grid; @@ -118,3 +119,80 @@ } } } + +.intro { + :global { + a { + @extend %link; + + &[hreflang], + &.download, + &.external { + @extend %link-with-icon; + } + + &[hreflang] { + @extend %link-with-lang; + } + + &[hreflang]:not(.download, .external) { + --is-icon-hidden: ""; + } + + &.download { + @extend %download-link; + } + + &.external { + @extend %external-link; + } + + &.download, + &.external { + &:not([hreflang]) { + --is-lang-hidden: ""; + } + } + + &.external.download { + @extend %external-download-link; + } + } + } +} + +:global([data-theme="light"]) { + :local { + .intro { + :global { + a { + &.download { + @extend %light-download-link; + } + + &.external { + @extend %light-external-link; + } + } + } + } + } +} + +:global([data-theme="dark"]) { + :local { + .intro { + :global { + a { + &.download { + @extend %dark-download-link; + } + + &.external { + @extend %dark-external-link; + } + } + } + } + } +} diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx index d66af75..e21e4c7 100644 --- a/src/components/organisms/layout/summary.tsx +++ b/src/components/organisms/layout/summary.tsx @@ -117,9 +117,11 @@ export const Summary: FC<SummaryProps> = ({ </Link> </header> <div className={styles.body}> - {/* eslint-disable-next-line react/no-danger -- Not safe but intro can - * contains links or formatting so we need it. */} - <div dangerouslySetInnerHTML={{ __html: intro }} /> + <div + className={styles.intro} + // eslint-disable-next-line react/no-danger + dangerouslySetInnerHTML={{ __html: intro }} + /> <ButtonLink className={styles['read-more']} to={url}> <> {readMore} diff --git a/src/content b/src/content -Subproject c7236df956a4f9e242126540c175ac5dfd0b9c7 +Subproject 0a5267ca7df1b6600741aa172ffdfe7b4f762d9 diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx index 5882ff6..b23c7a2 100644 --- a/src/pages/cv.tsx +++ b/src/pages/cv.tsx @@ -38,7 +38,7 @@ const ExternalLink = ({ href = '', ...props }: AnchorHTMLAttributes<HTMLAnchorElement>) => ( - <Link {...props} external={true} href={href}> + <Link {...props} isExternal href={href}> {children} </Link> ); @@ -172,7 +172,7 @@ const CVPage: NextPageWithLayout = () => { }, { link: (chunks: ReactNode) => ( - <Link download={true} href={file}> + <Link href={file} isDownload> {chunks} </Link> ), diff --git a/src/styles/abstracts/_placeholders.scss b/src/styles/abstracts/_placeholders.scss index c978f96..04522d7 100644 --- a/src/styles/abstracts/_placeholders.scss +++ b/src/styles/abstracts/_placeholders.scss @@ -3,4 +3,5 @@ @forward "./placeholders/clearfix"; @forward "./placeholders/headings"; @forward "./placeholders/layout"; +@forward "./placeholders/links"; @forward "./placeholders/lists"; diff --git a/src/styles/abstracts/functions/_encode.scss b/src/styles/abstracts/functions/_encode.scss index 4350185..388d106 100644 --- a/src/styles/abstracts/functions/_encode.scss +++ b/src/styles/abstracts/functions/_encode.scss @@ -4,7 +4,7 @@ /// @param {String} $svg A complete svg (`<svg>...</svg>`). /// @return The encoded svg, ready to use for background-image. @function encode-svg($svg) { - $svg-encoding: (("<", "%3C"), (">", "%3E"), ("#", "%23")); + $svg-encoding: (('"', "'"), ("<", "%3C"), (">", "%3E"), ("#", "%23")); @each $char, $encoded in $svg-encoding { $svg: fun.str-replace($svg, $char, $encoded); diff --git a/src/styles/abstracts/placeholders/_links.scss b/src/styles/abstracts/placeholders/_links.scss new file mode 100644 index 0000000..a230e70 --- /dev/null +++ b/src/styles/abstracts/placeholders/_links.scss @@ -0,0 +1,102 @@ +@use "../../abstracts/functions" as fun; +@use "../../abstracts/variables" as var; + +%link { + background: linear-gradient(to top, var(--color-primary) 50%, transparent 50%) + 0 0 / 100% 201% no-repeat; + color: var(--color-primary); + text-decoration-thickness: 0.15em; + text-underline-offset: 20%; + transition: + all 0.3s linear 0s, + text-decoration 0.18s ease-in-out 0s; + + &:hover { + color: var(--color-primary-light); + text-decoration-thickness: 0.25em; + } + + &:focus { + background-position: 0 100%; + color: var(--color-fg-inverted); + } + + &:active { + background-position: 0 0; + color: var(--color-primary-dark); + text-decoration-thickness: 18%; + } +} + +%link-with-icon { + &::after { + display: inline-block; + content: var(--is-lang-hidden, "\0000a0" var(--lang-icon, "")) + var(--is-icon-hidden, "\0000a0" var(--link-icon, "")); + font-size: var(--font-size-sm); + } +} + +%link-with-lang { + --lang-icon: "[" attr(hreflang) "]"; +} + +%light-download-link { + // Prettier is removing spacing between attributes. + // prettier-ignore + --download-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z" /><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z" /></svg>')}'); + + &:focus:not(:active) { + // Prettier is removing spacing between attributes. + // prettier-ignore + --download-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z" /><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z" /></svg>')}'); + } +} + +%dark-download-link { + // Prettier is removing spacing between attributes. + // prettier-ignore + --download-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z" /><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z" /></svg>')}'); + + &:focus:not(:active) { + // Prettier is removing spacing between attributes. + // prettier-ignore + --download-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z" /><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z" /></svg>')}'); + } +} + +%light-external-link { + // Prettier is removing spacing between attributes. + // prettier-ignore + --external-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')}'); + + &:focus:not(:active) { + // Prettier is removing spacing between attributes. + // prettier-ignore + --external-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')}'); + } +} + +%dark-external-link { + // Prettier is removing spacing between attributes. + // prettier-ignore + --external-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')}'); + + &:focus:not(:active) { + // Prettier is removing spacing between attributes. + // prettier-ignore + --external-icon: url('#{fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')}'); + } +} + +%download-link { + --link-icon: var(--download-icon); +} + +%external-link { + --link-icon: var(--external-icon); +} + +%external-download-link { + --link-icon: var(--download-icon) "\0000a0" var(--external-icon); +} diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss index c98533e..170f246 100644 --- a/src/styles/base/_typography.scss +++ b/src/styles/base/_typography.scss @@ -9,33 +9,6 @@ small { font-size: var(--font-size-sm); } -a { - background: linear-gradient(to top, var(--color-primary) 50%, transparent 50%) - 0 0 / 100% 201% no-repeat; - color: var(--color-primary); - text-decoration-thickness: 0.15em; - text-underline-offset: 20%; - transition: - all 0.3s linear 0s, - text-decoration 0.18s ease-in-out 0s; - - &:hover { - color: var(--color-primary-light); - text-decoration-thickness: 0.25em; - } - - &:focus { - background-position: 0 100%; - color: var(--color-fg-inverted); - } - - &:active { - background-position: 0 0; - color: var(--color-primary-dark); - text-decoration-thickness: 18%; - } -} - button, input, optgroup, diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss index 088718f..068826f 100644 --- a/src/styles/pages/article.module.scss +++ b/src/styles/pages/article.module.scss @@ -1,5 +1,6 @@ @use "../abstracts/functions" as fun; @use "../abstracts/mixins" as mix; +@use "../abstracts/placeholders"; @use "partials/article-headings"; @use "partials/article-links"; @use "partials/article-lists"; @@ -28,6 +29,42 @@ } } +:global([data-theme="light"]) { + :local { + .body { + :global { + a { + &.download { + @extend %light-download-link; + } + + &.external { + @extend %light-external-link; + } + } + } + } + } +} + +:global([data-theme="dark"]) { + :local { + .body { + :global { + a { + &.download { + @extend %dark-download-link; + } + + &.external { + @extend %dark-external-link; + } + } + } + } + } +} + .widget { @include mix.media("screen") { @include mix.dimensions("md") { diff --git a/src/styles/pages/partials/_article-links.scss b/src/styles/pages/partials/_article-links.scss index e88b81e..1627220 100644 --- a/src/styles/pages/partials/_article-links.scss +++ b/src/styles/pages/partials/_article-links.scss @@ -1,204 +1,40 @@ -@use "../../abstracts/functions" as fun; -@use "../../abstracts/variables" as var; +@use "../../abstracts/placeholders"; @mixin styles { a { - &[hreflang] { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ + @extend %link; - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]"; - font-size: var(--font-size-sm); - } - } - } - - /* stylelint-disable no-descending-specificity */ - a.download { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); + &[hreflang], + &.download, + &.external { + @extend %link-with-icon; } &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - } - } - - a.external { - &::after { - display: inline-block; - - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); + @extend %link-with-lang; } - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); + &[hreflang]:not(.download, .external) { + --is-icon-hidden: ""; } - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } + &.download { + @extend %download-link; } - } - a.external.download { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); + &.external { + @extend %external-link; } - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); + &.download, + &.external { + &:not([hreflang]) { + --is-lang-hidden: ""; } } - } - - [data-theme="dark"] { - a.download { - &::after { - /* Prettier is removing spacing between content parts. */ - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')); - } - } - } - - a.external { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } - } - - a.external.download { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &[hreflang] { - &::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - - &:focus:not(:active)::after { - /* Prettier is removing spacing between content parts. */ - - /* prettier-ignore */ - content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$dark-theme_black}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>')) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$dark-theme_black}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$dark-theme_black}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>')); - } - } + &.external.download { + @extend %external-download-link; } } } -/* stylelint-enable no-descending-specificity */ |
