diff --git a/projects/website/_src/_sass/_base.scss b/projects/website/_src/_sass/_base.scss new file mode 100644 index 00000000..6415dde5 --- /dev/null +++ b/projects/website/_src/_sass/_base.scss @@ -0,0 +1,281 @@ +// Base +* { + box-sizing: border-box; +} + +button, +input, +select, +textarea { + padding: 0.5rem 0.65rem; + border: 1px solid var(--border); + border-radius: 0; + color: var(--text-main); + background: var(--surface-container); + font: inherit; + transition: border-color 130ms linear, box-shadow 130ms linear, background-color 130ms linear; + + &:hover { + border-color: var(--primary); + box-shadow: var(--glow-soft); + } + + &:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 2px; + box-shadow: var(--glow-strong); + } +} + +button { + cursor: pointer; + transition: border-color 130ms linear, box-shadow 130ms linear, background-color 130ms linear, transform 130ms ease-out; + + &:hover, + &:focus-visible { + transform: translateY(-1px); + } + + &:active { + box-shadow: var(--glow-soft); + transform: translateY(0) scale(0.97); + } +} + +h2, +h3, +h4, +h5, +h6 { + margin: 2rem 0 0.75rem; + color: var(--text-bright); + line-height: 1.3; +} + +h2 { + font-size: 2.3rem; +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1.15rem; +} + +h5, +h6 { + font-size: 1rem; +} + +:is(h2, h3, h4, h5, h6) { + .heading-permalink { + display: inline-block; + margin-left: 0.4em; + color: var(--primary); + font-size: 0.55em; + line-height: 1; + opacity: 0; + text-decoration: none; + text-shadow: var(--glow-soft); + transform: scale(0.75); + transition: opacity 130ms linear, text-shadow 130ms linear, transform 130ms ease-out; + vertical-align: middle; + } + + .heading-permalink:focus-visible { + opacity: 1; + text-shadow: var(--glow-strong); + transform: scale(1); + } + + @media (hover: hover) { + &:hover .heading-permalink { + opacity: 1; + text-shadow: var(--glow-strong); + transform: scale(1); + } + } +} + +ul, +ol { + padding-left: 1.35rem; +} + +li + li { + margin-top: 0.35rem; +} + +blockquote { + margin: 1.5rem 0; + padding: 0.5rem 1rem; + border-left: 3px solid var(--primary); + color: var(--text-muted); +} + +code, +kbd, +samp { + padding: 0.15rem 0.3rem; + background: var(--surface-container); + color: var(--text-main); + font-size: 0.9em; +} + +pre { + overflow-x: auto; + padding: 1rem; + background: var(--surface-container-low); + + code { + padding: 0; + background: transparent; + } +} + +hr { + border: 0; + border-top: 1px solid var(--border); +} + +img, +video { + max-width: 100%; + height: auto; +} + +.publication section > p > img:only-child { + display: block; + padding: 0.75rem; + border: 1px solid #777; + background: #fff; + color-scheme: light; +} + +table { + display: block; + width: 100%; + overflow-x: auto; + border-collapse: collapse; +} + +th, +td { + padding: 0.5rem; + border-bottom: 1px solid var(--border); + text-align: left; + vertical-align: top; +} + +fieldset { + margin: 1.5rem 0; + padding: 1rem; + border: 1px solid var(--border); +} + +details { + margin: 1rem 0; + padding: 0.75rem 1rem; + border: 1px solid var(--border); +} + +summary { + cursor: pointer; +} + +address { + font-style: normal; +} + +html { + background: var(--background-body); + color: var(--text-main); + font-size: 16px; + line-height: 1.55; +} + +body { + width: var(--page-width); + max-width: none; + margin: 0 auto; + padding: 1.4rem 0 2.6rem; + background: var(--background-body); + color: var(--text-main); + font-size: 17px; + line-height: 1.6; + + @media (max-width: 700px) { + padding-top: 1.1rem; + } + + &::before, + &::after { + position: fixed; + inset: 0; + z-index: 0; + background-image: + radial-gradient(circle, color-mix(in srgb, var(--primary) 72%, transparent) 0 0.065rem, color-mix(in srgb, var(--primary) 18%, transparent) 0.1rem, transparent 0.2rem), + radial-gradient(circle, color-mix(in srgb, var(--primary) 58%, transparent) 0 0.055rem, color-mix(in srgb, var(--primary) 14%, transparent) 0.09rem, transparent 0.18rem); + background-position: 2rem 3rem, 11rem 8rem; + background-size: 23rem 19rem, 31rem 29rem; + content: ""; + opacity: 0.46; + pointer-events: none; + animation: ambient-sparks 9s ease-in-out infinite alternate; + } + + &::after { + background-position: 7rem 13rem, 19rem 4rem; + background-size: 37rem 31rem, 29rem 41rem; + opacity: 0.3; + animation-delay: -4.5s; + animation-duration: 13s; + } + + > * { + position: relative; + z-index: 1; + } +} + +::selection { + background: var(--selection); + color: var(--text-bright); +} + +:target { + background: var(--background-alt); +} + +:is(h1, h2, h3, h4, h5, h6):target { + animation: target-glint 700ms ease-out; +} + +a { + color: var(--links); + text-decoration-color: color-mix(in srgb, currentColor 65%, transparent); + text-decoration-thickness: 1px; + text-underline-offset: 0.14em; + transition: color 130ms linear, text-decoration-color 130ms linear, text-decoration-thickness 130ms linear, text-underline-offset 130ms ease-out; + + &:hover, + &:focus-visible { + color: var(--primary); + text-decoration-color: var(--primary); + text-decoration-thickness: 2px; + text-underline-offset: 0.2em; + } + + &:focus-visible { + outline: 1px solid var(--primary); + outline-offset: 4px; + } + + &[href^="mailto:"]::before, + &[href^="tel:"]::before, + &[href^="sms:"]::before { + content: ""; + } +} diff --git a/projects/website/_src/_sass/_content.scss b/projects/website/_src/_sass/_content.scss new file mode 100644 index 00000000..aa211d32 --- /dev/null +++ b/projects/website/_src/_sass/_content.scss @@ -0,0 +1,82 @@ +// Content +h1 { + max-width: 56.25rem; + margin: 0 0 1rem; + color: var(--text-bright); +} + +p { + margin-top: 0; +} + +main > article { + max-width: var(--content-width); + margin: 0 auto; + + > header { + margin: 0; + padding: 2.25rem 0 1.5rem; + + p { + margin: 0.2rem 0 0; + color: var(--text-muted); + font-size: 0.9rem; + } + } + + > section { + > p { + margin-bottom: 1.15rem; + } + + > h2 { + margin: 2.6rem 0 1rem; + padding-top: 1rem; + border-top: 1px solid var(--border); + border-image: var(--starlight-rule); + color: var(--primary); + font-family: "Fira Sans", system-ui, sans-serif; + font-size: 2rem; + font-weight: 700; + line-height: 1.2; + + &:first-of-type { + margin-top: 0; + padding-top: 0; + border-top: 0; + border-image: none; + } + } + + > h3 { + margin: 2rem 0 0.7rem; + color: var(--primary); + font-size: 1.4rem; + font-weight: 700; + line-height: 1.35; + } + + > h4 { + margin: 1.6rem 0 0.55rem; + color: var(--text-main); + font-size: 1.05rem; + font-weight: 700; + line-height: 1.4; + } + + > ul { + margin: 1rem 0 1.5rem; + padding-left: 1.35rem; + + li + li { + margin-top: 0.7rem; + } + } + + } +} + +main > article:not(:has(> section > article)) > section { + max-width: var(--document-width); + margin-inline: auto; +} diff --git a/projects/website/_src/_sass/_footer.scss b/projects/website/_src/_sass/_footer.scss new file mode 100644 index 00000000..5855c2df --- /dev/null +++ b/projects/website/_src/_sass/_footer.scss @@ -0,0 +1,204 @@ +// Footer and theme picker +body > footer { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + gap: 1rem; + margin-top: 1.4rem; + padding-top: 1.25rem; + border-top: 1px solid var(--border); + border-image: var(--starlight-rule); + color: var(--text-muted); + font-size: 0.875rem; + + @media (max-width: 700px) { + grid-template-columns: 1fr; + } + + > address { + grid-column: 1 / -1; + grid-row: 2; + padding-top: 0.75rem; + border-top: 1px solid var(--border); + border-image: var(--starlight-rule); + font-style: normal; + } + + > nav { + grid-column: 3; + grid-row: 1; + justify-self: end; + text-align: right; + } + + > .copyleft { + grid-column: 1; + grid-row: 1; + margin: 0; + } + + > address, + > nav, + > .copyleft { + @media (max-width: 700px) { + grid-column: 1; + grid-row: auto; + justify-self: start; + text-align: left; + } + } + + > address { + @media (max-width: 700px) { + padding-top: 0; + border-top: 0; + } + } + + > .theme-constellation { + grid-column: 1 / -1; + grid-row: 3; + margin-top: 0.4rem; + + @media (max-width: 700px) { + grid-row: auto; + } + padding-top: 1rem; + border-top: 1px solid var(--border); + border-image: var(--starlight-rule); + + &[hidden] { + display: none; + } + + > p { + margin: -0.35rem 0 0; + text-align: center; + } + + .stars { + position: relative; + width: 100%; + height: clamp(10rem, 22vw, 13rem); + + svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + overflow: visible; + + path { + fill: none; + stroke: var(--border); + stroke-width: 0.45; + vector-effect: non-scaling-stroke; + } + } + + .placeholder-star { + position: absolute; + top: var(--y); + left: var(--x); + color: var(--text-muted); + font-size: 0.75rem; + line-height: 1; + opacity: 0.6; + text-shadow: 0 0 0.5rem color-mix(in srgb, var(--text-muted) 35%, transparent); + translate: -50% -50%; + + &::before { + content: "✦"; + } + } + + button { + --star-color: var(--star-dark); + position: absolute; + top: var(--y); + + @media (prefers-color-scheme: light) { + --star-color: var(--star-light); + } + left: var(--x); + width: 3rem; + height: 3rem; + padding: 0; + border: 0; + border-radius: 50%; + background: transparent; + touch-action: manipulation; + translate: -50% -50%; + + &::before { + position: absolute; + bottom: calc(100% - 0.15rem); + left: 50%; + z-index: 1; + padding: 0.15rem 0.35rem; + border: 1px solid var(--border); + color: var(--text-main); + background: var(--surface-container-high); + content: attr(data-label); + opacity: 0; + pointer-events: none; + transform: translate(-50%, 0.2rem); + transition: opacity 120ms linear, transform 120ms ease-out; + white-space: nowrap; + } + + &:hover::before, + &:focus-visible::before { + opacity: 1; + transform: translate(-50%, 0); + } + + &:focus-visible, + &[aria-pressed="true"] { + outline: 0; + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--star-color) 85%, transparent), 0 0 0.45rem color-mix(in srgb, var(--star-color) 25%, transparent); + } + + span { + position: absolute; + top: 50%; + left: 50%; + color: var(--star-color); + font-size: 1.05rem; + line-height: 1; + text-shadow: 0 0 0.75rem color-mix(in srgb, var(--star-color) 45%, transparent); + transform: translate(-50%, -50%); + transition: text-shadow 130ms linear, transform 130ms ease-out; + + &::before { + content: "✦"; + } + } + + &:hover span, + &:focus-visible span { + text-shadow: 0 0 1rem var(--star-color); + transform: translate(-50%, -50%) scale(1.2); + } + + &:active span { + transform: translate(-50%, -50%) scale(0.9); + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &[aria-pressed="true"] span { + text-shadow: 0 0 0.2rem var(--background-body), 0 0 0.45rem var(--star-color), 0 0 1rem var(--star-color); + animation: star-glint 360ms ease-out; + } + } + } + } + + > .print-site-link { + display: none; + } +} diff --git a/projects/website/_src/_sass/_header.scss b/projects/website/_src/_sass/_header.scss new file mode 100644 index 00000000..a1299f16 --- /dev/null +++ b/projects/website/_src/_sass/_header.scss @@ -0,0 +1,127 @@ +// Site header +body > header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 1.5rem; + margin: 0; + padding: 0 0 0.75rem; + border-bottom: 1px solid var(--border); + border-image: var(--starlight-rule); + + > a[rel="home"] { + color: var(--primary); + font-family: "Fira Sans Condensed", "Arial Narrow", sans-serif; + font-size: 1.7rem; + font-weight: 600; + letter-spacing: 0.015em; + text-decoration: none; + transition: color 130ms linear, transform 130ms ease-out; + + &:hover, + &:focus-visible { + transform: translateY(-1px); + } + + &:active { + transform: translateY(0) scale(0.98); + } + + > span { + display: inline-block; + font-size: 0.65em; + text-shadow: var(--glow-soft); + transition: text-shadow 130ms linear, transform 130ms ease-out; + vertical-align: 0.12em; + } + + &:hover > span, + &:focus-visible > span { + text-shadow: var(--glow-strong); + transform: scale(1.2); + } + } + + @media (max-width: 700px) { + align-items: flex-start; + flex-direction: column; + gap: 0.8rem; + } +} + +body > header nav { + margin: 0; + + @media (max-width: 700px) { + width: 100%; + overflow-x: auto; + } + + ul { + display: flex; + gap: clamp(1.25rem, 2.5vw, 2.2rem); + margin: 0; + padding: 0; + list-style: none; + + @media (max-width: 700px) { + justify-content: flex-start; + gap: clamp(1rem, 4vw, 1.5rem); + } + } + + li { + margin: 0; + } + + a { + position: relative; + display: inline-block; + padding: 0.25rem 0.1rem; + color: var(--primary); + background-image: linear-gradient(var(--primary), var(--primary)); + background-position: left calc(100% - 0.1rem); + background-repeat: no-repeat; + background-size: 0 1px; + font-size: 0.95rem; + font-weight: 700; + letter-spacing: 0.01em; + line-height: 1.5; + text-decoration: none; + transition: color 130ms linear, background-size 130ms ease-out, transform 130ms ease-out; + + &::after { + position: absolute; + right: -0.1rem; + bottom: 0; + width: 0.2rem; + height: 0.2rem; + background: currentColor; + content: ""; + opacity: 0; + pointer-events: none; + transform: rotate(45deg) scale(0); + transition: opacity 100ms linear, transform 130ms ease-out; + } + + &:hover, + &:focus-visible { + background-size: 100% 1px; + transform: translateY(-1px); + + &::after { + opacity: 1; + transform: rotate(45deg) scale(1); + } + } + + &:active { + transform: translateY(0) scale(0.96); + } + + &[aria-current="page"] { + color: var(--text-main); + background-size: 100% 1px; + } + } +} diff --git a/projects/website/_src/_sass/_listings.scss b/projects/website/_src/_sass/_listings.scss new file mode 100644 index 00000000..662e428c --- /dev/null +++ b/projects/website/_src/_sass/_listings.scss @@ -0,0 +1,115 @@ +// Article listings +main > article:has(> section > article) { + position: relative; + + > section { + > article { + position: relative; + display: grid; + grid-template-columns: 11.5625rem minmax(0, 1fr); + gap: 0 2.5rem; + margin: 0; + padding: 1.75rem 0 1.9rem; + border-top: 1px solid var(--border); + + @media (max-width: 700px) { + grid-template-columns: 1fr; + gap: 0.8rem; + } + + &:last-child { + border-bottom: 1px solid var(--border); + } + + > header { + display: contents; + + > p { + grid-column: 1; + grid-row: 1; + margin: 0; + color: var(--text-muted); + font-size: 0.9rem; + line-height: 1.5; + + small { + display: block; + color: var(--primary); + font-size: inherit; + font-weight: 700; + } + } + } + + h2 { + grid-column: 2; + grid-row: 1; + margin: 0 0 0.7rem; + font-family: "Fira Sans", system-ui, sans-serif; + font-size: clamp(1.35rem, 2vw, 1.75rem); + font-weight: 700; + line-height: 1.3; + + a { + color: var(--primary); + text-decoration-color: transparent; + + &:hover, + &:focus-visible { + text-decoration-color: var(--primary); + text-decoration-thickness: 2px; + text-underline-offset: 0.2em; + } + } + } + + > section, + > footer { + grid-column: 2; + + p { + margin-bottom: 0; + } + } + + > section { + grid-row: 2; + } + + > footer { + grid-row: 3; + margin-top: 0.75rem; + } + + @media (max-width: 700px) { + > header > p, + h2, + > section, + > footer { + grid-column: 1; + } + + > header > p { + grid-row: 1; + + small { + display: inline; + margin-left: 0.5rem; + } + } + + h2 { + grid-row: 2; + } + + > section { + grid-row: 3; + } + + > footer { + grid-row: 4; + } + } + } + } +} diff --git a/projects/website/_src/_sass/_motion.scss b/projects/website/_src/_sass/_motion.scss new file mode 100644 index 00000000..800108ef --- /dev/null +++ b/projects/website/_src/_sass/_motion.scss @@ -0,0 +1,49 @@ +// Motion +@keyframes ambient-sparks { + 0%, + 18% { + opacity: 0.24; + transform: translate(0); + } + + 48% { + opacity: 0.5; + } + + 72%, + 100% { + opacity: 0.32; + transform: translate(0.18rem, -0.12rem); + } +} + +@keyframes star-glint { + 50% { + transform: translate(-50%, -50%) scale(1.55); + } +} + +@keyframes target-glint { + 35% { + color: var(--primary); + text-shadow: var(--glow-strong); + } +} + +@media (prefers-reduced-motion: reduce) { + body::before, + body::after, + :is(h1, h2, h3, h4, h5, h6):target, + a, + .heading-permalink, + body > header nav a::after, + button, + input, + select, + textarea, + .theme-constellation .stars button::before, + .theme-constellation .stars button span { + animation: none !important; + transition: none !important; + } +} diff --git a/projects/website/_src/_sass/_print.scss b/projects/website/_src/_sass/_print.scss new file mode 100644 index 00000000..1cd59635 --- /dev/null +++ b/projects/website/_src/_sass/_print.scss @@ -0,0 +1,41 @@ +// Printing +@media print { + :root { + color-scheme: light; + } + + * { + print-color-adjust: exact; + } + + body { + width: 100%; + margin: 0 auto; + padding: 0; + font-size: 11pt; + + &::before, + &::after { + display: none; + } + } + + .heading-permalink, + body > header, + body > footer > .copyleft, + body > footer > .theme-constellation { + display: none; + } + + main > article > section > :is(h2, h3, h4) { + break-after: avoid; + } + + body > footer > .print-site-link { + display: block; + } +} + +@view-transition { + navigation: auto; +} diff --git a/projects/website/_src/_sass/_tokens.scss b/projects/website/_src/_sass/_tokens.scss new file mode 100644 index 00000000..e7167e76 --- /dev/null +++ b/projects/website/_src/_sass/_tokens.scss @@ -0,0 +1,30 @@ +// Color roles +:root { + color-scheme: light dark; + --background-body: color-mix(in srgb, var(--surface_container_lowest) 88%, var(--primary_container) 12%); + --background-alt: color-mix(in srgb, var(--surface_container_high) 82%, var(--primary_container) 18%); + --selection: color-mix(in srgb, var(--primary) 30%, transparent 70%); + --text-main: var(--on_background); + --text-bright: var(--on_surface); + --text-muted: var(--on_surface_variant); + --links: var(--blue); + --border: color-mix(in srgb, var(--outline_variant) 80%, var(--primary) 20%); + --surface-container: var(--surface_container); + --surface-container-low: var(--surface_container_low); + --surface-container-high: var(--surface_container_high); + --glow-soft: 0 0 0.75rem color-mix(in srgb, var(--primary) 30%, transparent); + --glow-strong: 0 0 1rem color-mix(in srgb, var(--primary) 65%, transparent); + --starlight-rule: linear-gradient(90deg, var(--border), color-mix(in srgb, var(--primary) 75%, var(--border)), var(--border)) 1; + --page-width: min(56.25rem, calc(100vw - 3rem)); + --content-width: 56.25rem; + --document-width: 72ch; + + @media (prefers-color-scheme: light) { + --background-body: color-mix(in srgb, var(--surface_container_lowest) 89%, var(--primary) 11%); + --background-alt: color-mix(in srgb, var(--surface_container_high) 79%, var(--primary) 21%); + } + + @media (max-width: 700px) { + --page-width: calc(100vw - 2rem); + } +} diff --git a/projects/website/_src/_sass/main.scss b/projects/website/_src/_sass/main.scss index 16c988d0..1216c760 100644 --- a/projects/website/_src/_sass/main.scss +++ b/projects/website/_src/_sass/main.scss @@ -1,950 +1,11 @@ -@import "vendor/normalize"; -@import "highlight"; -@import "typography"; - -// Color roles -:root { - color-scheme: light dark; - --background-body: color-mix(in srgb, var(--surface_container_lowest) 88%, var(--primary_container) 12%); - --background-alt: color-mix(in srgb, var(--surface_container_high) 82%, var(--primary_container) 18%); - --selection: color-mix(in srgb, var(--primary) 30%, transparent 70%); - --text-main: var(--on_background); - --text-bright: var(--on_surface); - --text-muted: var(--on_surface_variant); - --links: var(--blue); - --border: color-mix(in srgb, var(--outline_variant) 80%, var(--primary) 20%); - --surface-container: var(--surface_container); - --surface-container-low: var(--surface_container_low); - --surface-container-high: var(--surface_container_high); - --glow-soft: 0 0 0.75rem color-mix(in srgb, var(--primary) 30%, transparent); - --glow-strong: 0 0 1rem color-mix(in srgb, var(--primary) 65%, transparent); - --starlight-rule: linear-gradient(90deg, var(--border), color-mix(in srgb, var(--primary) 75%, var(--border)), var(--border)) 1; - --page-width: min(56.25rem, calc(100vw - 3rem)); - --content-width: 56.25rem; - --document-width: 72ch; - - @media (prefers-color-scheme: light) { - --background-body: color-mix(in srgb, var(--surface_container_lowest) 89%, var(--primary) 11%); - --background-alt: color-mix(in srgb, var(--surface_container_high) 79%, var(--primary) 21%); - } - - @media (max-width: 700px) { - --page-width: calc(100vw - 2rem); - } -} - -// Base -* { - box-sizing: border-box; -} - -button, -input, -select, -textarea { - padding: 0.5rem 0.65rem; - border: 1px solid var(--border); - border-radius: 0; - color: var(--text-main); - background: var(--surface-container); - font: inherit; - transition: border-color 130ms linear, box-shadow 130ms linear, background-color 130ms linear; - - &:hover { - border-color: var(--primary); - box-shadow: var(--glow-soft); - } - - &:focus-visible { - outline: 2px solid var(--primary); - outline-offset: 2px; - box-shadow: var(--glow-strong); - } -} - -button { - cursor: pointer; - transition: border-color 130ms linear, box-shadow 130ms linear, background-color 130ms linear, transform 130ms ease-out; - - &:hover, - &:focus-visible { - transform: translateY(-1px); - } - - &:active { - box-shadow: var(--glow-soft); - transform: translateY(0) scale(0.97); - } -} - -h2, -h3, -h4, -h5, -h6 { - margin: 2rem 0 0.75rem; - color: var(--text-bright); - line-height: 1.3; -} - -h2 { - font-size: 2.3rem; -} - -h3 { - font-size: 1.5rem; -} - -h4 { - font-size: 1.15rem; -} - -h5, -h6 { - font-size: 1rem; -} - -:is(h2, h3, h4, h5, h6) { - .heading-permalink { - display: inline-block; - margin-left: 0.4em; - color: var(--primary); - font-size: 0.55em; - line-height: 1; - opacity: 0; - text-decoration: none; - text-shadow: var(--glow-soft); - transform: scale(0.75); - transition: opacity 130ms linear, text-shadow 130ms linear, transform 130ms ease-out; - vertical-align: middle; - - @media print { - display: none; - } - } - - .heading-permalink:focus-visible { - opacity: 1; - text-shadow: var(--glow-strong); - transform: scale(1); - } - - @media (hover: hover) { - &:hover .heading-permalink { - opacity: 1; - text-shadow: var(--glow-strong); - transform: scale(1); - } - } -} - -ul, -ol { - padding-left: 1.35rem; -} - -li + li { - margin-top: 0.35rem; -} - -blockquote { - margin: 1.5rem 0; - padding: 0.5rem 1rem; - border-left: 3px solid var(--primary); - color: var(--text-muted); -} - -code, -kbd, -samp { - padding: 0.15rem 0.3rem; - background: var(--surface-container); - color: var(--text-main); - font-size: 0.9em; -} - -pre { - overflow-x: auto; - padding: 1rem; - background: var(--surface-container-low); - - code { - padding: 0; - background: transparent; - } -} - -hr { - border: 0; - border-top: 1px solid var(--border); -} - -img, -video { - max-width: 100%; - height: auto; -} - -.publication section > p > img:only-child { - display: block; - padding: 0.75rem; - border: 1px solid #777; - background: #fff; - color-scheme: light; -} - -table { - display: block; - width: 100%; - overflow-x: auto; - border-collapse: collapse; -} - -th, -td { - padding: 0.5rem; - border-bottom: 1px solid var(--border); - text-align: left; - vertical-align: top; -} - -fieldset { - margin: 1.5rem 0; - padding: 1rem; - border: 1px solid var(--border); -} - -details { - margin: 1rem 0; - padding: 0.75rem 1rem; - border: 1px solid var(--border); -} - -summary { - cursor: pointer; -} - -address { - font-style: normal; -} - -html { - background: var(--background-body); - color: var(--text-main); - font-size: 16px; - line-height: 1.55; -} - -body { - width: var(--page-width); - max-width: none; - margin: 0 auto; - padding: 1.4rem 0 2.6rem; - background: var(--background-body); - color: var(--text-main); - font-size: 17px; - line-height: 1.6; - - @media (max-width: 700px) { - padding-top: 1.1rem; - } - - &::before, - &::after { - position: fixed; - inset: 0; - z-index: 0; - background-image: - radial-gradient(circle, color-mix(in srgb, var(--primary) 72%, transparent) 0 0.065rem, color-mix(in srgb, var(--primary) 18%, transparent) 0.1rem, transparent 0.2rem), - radial-gradient(circle, color-mix(in srgb, var(--primary) 58%, transparent) 0 0.055rem, color-mix(in srgb, var(--primary) 14%, transparent) 0.09rem, transparent 0.18rem); - background-position: 2rem 3rem, 11rem 8rem; - background-size: 23rem 19rem, 31rem 29rem; - content: ""; - opacity: 0.46; - pointer-events: none; - animation: ambient-sparks 9s ease-in-out infinite alternate; - } - - &::after { - background-position: 7rem 13rem, 19rem 4rem; - background-size: 37rem 31rem, 29rem 41rem; - opacity: 0.3; - animation-delay: -4.5s; - animation-duration: 13s; - } - - > * { - position: relative; - z-index: 1; - } - - @media print { - width: 100%; - margin: 0 auto; - padding: 0; - font-size: 11pt; - - &::before, - &::after { - display: none; - } - } -} - -::selection { - background: var(--selection); - color: var(--text-bright); -} - -:target { - background: var(--background-alt); -} - -:is(h1, h2, h3, h4, h5, h6):target { - animation: target-glint 700ms ease-out; -} - -a { - color: var(--links); - text-decoration-color: color-mix(in srgb, currentColor 65%, transparent); - text-decoration-thickness: 1px; - text-underline-offset: 0.14em; - transition: color 130ms linear, text-decoration-color 130ms linear, text-decoration-thickness 130ms linear, text-underline-offset 130ms ease-out; - - &:hover, - &:focus-visible { - color: var(--primary); - text-decoration-color: var(--primary); - text-decoration-thickness: 2px; - text-underline-offset: 0.2em; - } - - &:focus-visible { - outline: 1px solid var(--primary); - outline-offset: 4px; - } - - &[href^="mailto:"]::before, - &[href^="tel:"]::before, - &[href^="sms:"]::before { - content: ""; - } -} - -h1 { - max-width: 56.25rem; - margin: 0 0 1rem; - color: var(--text-bright); -} - -p { - margin-top: 0; -} - -main > article { - max-width: var(--content-width); - margin: 0 auto; - - > header { - margin: 0; - padding: 2.25rem 0 1.5rem; - - p { - margin: 0.2rem 0 0; - color: var(--text-muted); - font-size: 0.9rem; - } - } - - > section { - > p { - margin-bottom: 1.15rem; - } - - > h2 { - margin: 2.6rem 0 1rem; - padding-top: 1rem; - border-top: 1px solid var(--border); - border-image: var(--starlight-rule); - color: var(--primary); - font-family: "Fira Sans", system-ui, sans-serif; - font-size: 2rem; - font-weight: 700; - line-height: 1.2; - - &:first-of-type { - margin-top: 0; - padding-top: 0; - border-top: 0; - border-image: none; - } - } - - > h3 { - margin: 2rem 0 0.7rem; - color: var(--primary); - font-size: 1.4rem; - font-weight: 700; - line-height: 1.35; - } - - > h4 { - margin: 1.6rem 0 0.55rem; - color: var(--text-main); - font-size: 1.05rem; - font-weight: 700; - line-height: 1.4; - } - - > ul { - margin: 1rem 0 1.5rem; - padding-left: 1.35rem; - - li + li { - margin-top: 0.7rem; - } - } - - > h2, - > h3, - > h4 { - @media print { - break-after: avoid; - } - } - } -} - -main > article:not(:has(> section > article)) > section { - max-width: var(--document-width); - margin-inline: auto; -} - -// Site header -body > header { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 1.5rem; - margin: 0; - padding: 0 0 0.75rem; - border-bottom: 1px solid var(--border); - border-image: var(--starlight-rule); - - > a[rel="home"] { - color: var(--primary); - font-family: "Fira Sans Condensed", "Arial Narrow", sans-serif; - font-size: 1.7rem; - font-weight: 600; - letter-spacing: 0.015em; - text-decoration: none; - transition: color 130ms linear, transform 130ms ease-out; - - &:hover, - &:focus-visible { - transform: translateY(-1px); - } - - &:active { - transform: translateY(0) scale(0.98); - } - - > span { - display: inline-block; - font-size: 0.65em; - text-shadow: var(--glow-soft); - transition: text-shadow 130ms linear, transform 130ms ease-out; - vertical-align: 0.12em; - } - - &:hover > span, - &:focus-visible > span { - text-shadow: var(--glow-strong); - transform: scale(1.2); - } - } - - @media (max-width: 700px) { - align-items: flex-start; - flex-direction: column; - gap: 0.8rem; - } - - @media print { - display: none; - } -} - -body > header nav { - margin: 0; - - @media (max-width: 700px) { - width: 100%; - overflow-x: auto; - } - - ul { - display: flex; - gap: clamp(1.25rem, 2.5vw, 2.2rem); - margin: 0; - padding: 0; - list-style: none; - - @media (max-width: 700px) { - justify-content: flex-start; - gap: clamp(1rem, 4vw, 1.5rem); - } - } - - li { - margin: 0; - } - - a { - position: relative; - display: inline-block; - padding: 0.25rem 0.1rem; - color: var(--primary); - background-image: linear-gradient(var(--primary), var(--primary)); - background-position: left calc(100% - 0.1rem); - background-repeat: no-repeat; - background-size: 0 1px; - font-size: 0.95rem; - font-weight: 700; - letter-spacing: 0.01em; - line-height: 1.5; - text-decoration: none; - transition: color 130ms linear, background-size 130ms ease-out, transform 130ms ease-out; - - &::after { - position: absolute; - right: -0.1rem; - bottom: 0; - width: 0.2rem; - height: 0.2rem; - background: currentColor; - content: ""; - opacity: 0; - pointer-events: none; - transform: rotate(45deg) scale(0); - transition: opacity 100ms linear, transform 130ms ease-out; - } - - &:hover, - &:focus-visible { - background-size: 100% 1px; - transform: translateY(-1px); - - &::after { - opacity: 1; - transform: rotate(45deg) scale(1); - } - } - - &:active { - transform: translateY(0) scale(0.96); - } - - &[aria-current="page"] { - color: var(--text-main); - background-size: 100% 1px; - } - } -} - -// Article listings -main > article:has(> section > article) { - position: relative; - - > section { - > article { - position: relative; - display: grid; - grid-template-columns: 11.5625rem minmax(0, 1fr); - gap: 0 2.5rem; - margin: 0; - padding: 1.75rem 0 1.9rem; - border-top: 1px solid var(--border); - - @media (max-width: 700px) { - grid-template-columns: 1fr; - gap: 0.8rem; - } - - &:last-child { - border-bottom: 1px solid var(--border); - } - - > header { - display: contents; - - > p { - grid-column: 1; - grid-row: 1; - margin: 0; - color: var(--text-muted); - font-size: 0.9rem; - line-height: 1.5; - - small { - display: block; - color: var(--primary); - font-size: inherit; - font-weight: 700; - } - } - } - - h2 { - grid-column: 2; - grid-row: 1; - margin: 0 0 0.7rem; - font-family: "Fira Sans", system-ui, sans-serif; - font-size: clamp(1.35rem, 2vw, 1.75rem); - font-weight: 700; - line-height: 1.3; - - a { - color: var(--primary); - text-decoration-color: transparent; - - &:hover, - &:focus-visible { - text-decoration-color: var(--primary); - text-decoration-thickness: 2px; - text-underline-offset: 0.2em; - } - } - } - - > section, - > footer { - grid-column: 2; - - p { - margin-bottom: 0; - } - } - - > section { - grid-row: 2; - } - - > footer { - grid-row: 3; - margin-top: 0.75rem; - } - - @media (max-width: 700px) { - > header > p, - h2, - > section, - > footer { - grid-column: 1; - } - - > header > p { - grid-row: 1; - - small { - display: inline; - margin-left: 0.5rem; - } - } - - h2 { - grid-row: 2; - } - - > section { - grid-row: 3; - } - - > footer { - grid-row: 4; - } - } - } - } -} - -// Footer and theme picker -body > footer { - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: center; - gap: 1rem; - margin-top: 1.4rem; - padding-top: 1.25rem; - border-top: 1px solid var(--border); - border-image: var(--starlight-rule); - color: var(--text-muted); - font-size: 0.875rem; - - @media (max-width: 700px) { - grid-template-columns: 1fr; - } - - > address { - grid-column: 1 / -1; - grid-row: 2; - padding-top: 0.75rem; - border-top: 1px solid var(--border); - border-image: var(--starlight-rule); - font-style: normal; - } - - > nav { - grid-column: 3; - grid-row: 1; - justify-self: end; - text-align: right; - } - - > .copyleft { - grid-column: 1; - grid-row: 1; - margin: 0; - - @media print { - display: none; - } - } - - > address, - > nav, - > .copyleft { - @media (max-width: 700px) { - grid-column: 1; - grid-row: auto; - justify-self: start; - text-align: left; - } - } - - > address { - @media (max-width: 700px) { - padding-top: 0; - border-top: 0; - } - } - - > .theme-constellation { - grid-column: 1 / -1; - grid-row: 3; - margin-top: 0.4rem; - - @media (max-width: 700px) { - grid-row: auto; - } - padding-top: 1rem; - border-top: 1px solid var(--border); - border-image: var(--starlight-rule); - - &[hidden] { - display: none; - } - - > p { - margin: -0.35rem 0 0; - text-align: center; - } - - .stars { - position: relative; - width: 100%; - height: clamp(10rem, 22vw, 13rem); - - svg { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - overflow: visible; - - path { - fill: none; - stroke: var(--border); - stroke-width: 0.45; - vector-effect: non-scaling-stroke; - } - } - - .placeholder-star { - position: absolute; - top: var(--y); - left: var(--x); - color: var(--text-muted); - font-size: 0.75rem; - line-height: 1; - opacity: 0.6; - text-shadow: 0 0 0.5rem color-mix(in srgb, var(--text-muted) 35%, transparent); - translate: -50% -50%; - - &::before { - content: "✦"; - } - } - - button { - --star-color: var(--star-dark); - position: absolute; - top: var(--y); - - @media (prefers-color-scheme: light) { - --star-color: var(--star-light); - } - left: var(--x); - width: 3rem; - height: 3rem; - padding: 0; - border: 0; - border-radius: 50%; - background: transparent; - touch-action: manipulation; - translate: -50% -50%; - - &::before { - position: absolute; - bottom: calc(100% - 0.15rem); - left: 50%; - z-index: 1; - padding: 0.15rem 0.35rem; - border: 1px solid var(--border); - color: var(--text-main); - background: var(--surface-container-high); - content: attr(data-label); - opacity: 0; - pointer-events: none; - transform: translate(-50%, 0.2rem); - transition: opacity 120ms linear, transform 120ms ease-out; - white-space: nowrap; - } - - &:hover::before, - &:focus-visible::before { - opacity: 1; - transform: translate(-50%, 0); - } - - &:focus-visible, - &[aria-pressed="true"] { - outline: 0; - box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--star-color) 85%, transparent), 0 0 0.45rem color-mix(in srgb, var(--star-color) 25%, transparent); - } - - span { - position: absolute; - top: 50%; - left: 50%; - color: var(--star-color); - font-size: 1.05rem; - line-height: 1; - text-shadow: 0 0 0.75rem color-mix(in srgb, var(--star-color) 45%, transparent); - transform: translate(-50%, -50%); - transition: text-shadow 130ms linear, transform 130ms ease-out; - - &::before { - content: "✦"; - } - } - - &:hover span, - &:focus-visible span { - text-shadow: 0 0 1rem var(--star-color); - transform: translate(-50%, -50%) scale(1.2); - } - - &:active span { - transform: translate(-50%, -50%) scale(0.9); - } - - &:disabled { - cursor: not-allowed; - opacity: 0.5; - } - - &[aria-pressed="true"] span { - text-shadow: 0 0 0.2rem var(--background-body), 0 0 0.45rem var(--star-color), 0 0 1rem var(--star-color); - animation: star-glint 360ms ease-out; - } - } - } - - @media print { - display: none; - } - } - - > .print-site-link { - display: none; - - @media print { - display: block; - } - } -} - -@keyframes ambient-sparks { - 0%, - 18% { - opacity: 0.24; - transform: translate(0); - } - - 48% { - opacity: 0.5; - } - - 72%, - 100% { - opacity: 0.32; - transform: translate(0.18rem, -0.12rem); - } -} - -@keyframes star-glint { - 50% { - transform: translate(-50%, -50%) scale(1.55); - } -} - -@keyframes target-glint { - 35% { - color: var(--primary); - text-shadow: var(--glow-strong); - } -} - -@media (prefers-reduced-motion: reduce) { - body::before, - body::after, - :is(h1, h2, h3, h4, h5, h6):target, - a, - .heading-permalink, - body > header nav a::after, - button, - input, - select, - textarea, - .theme-constellation .stars button::before, - .theme-constellation .stars button span { - animation: none !important; - transition: none !important; - } -} - -// Printing -@media print { - :root { - color-scheme: light; - } - - * { - print-color-adjust: exact; - } -} - -@view-transition { - navigation: auto; -} +@use "vendor/normalize"; +@use "highlight"; +@use "typography"; +@use "tokens"; +@use "base"; +@use "content"; +@use "header"; +@use "listings"; +@use "footer"; +@use "motion"; +@use "print"; diff --git a/projects/website/_src/assets/style.scss b/projects/website/_src/assets/style.scss index 9f418944..381e071b 100644 --- a/projects/website/_src/assets/style.scss +++ b/projects/website/_src/assets/style.scss @@ -1,4 +1,4 @@ --- --- -@import "main"; +@use "main";