[UwU] Update all deps (#623)

This PR;

- Updates all deps
- Fixes linting errors caused by deps update
- Formats all files using new Prettier 3
This commit is contained in:
Corbin Crutchley
2023-07-26 17:33:43 -07:00
committed by GitHub
151 changed files with 5496 additions and 4590 deletions

View File

@@ -1,6 +1,7 @@
const tsRules = {
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-unused-vars": "off",
};
module.exports = {

View File

@@ -18,7 +18,7 @@ const fuse = new Fuse<ExtendedPostInfo>(
includeScore: true,
ignoreFieldNorm: true,
},
index
index,
);
export default async (req: VercelRequest, res: VercelResponse) => {

View File

@@ -68,7 +68,7 @@ export default defineConfig({
/* start remark plugins here */
[behead, { depth: 1 }],
[
remarkEmbedder as any,
remarkEmbedder,
{
transformers: [oembedTransformer, TwitchTransformer],
} as RemarkEmbedderOptions,

View File

@@ -40,7 +40,7 @@ async function generateEpubHTML(slug: string, content: string) {
createRehypePlugins({
format: "epub",
path: resolve(process.cwd(), `content/blog/${slug}/`),
})
}),
)
// Voids: [] is required for epub generation, and causes little/no harm for non-epub usage
.use(rehypeStringify, { allowDangerousHtml: true, voids: [] });
@@ -55,7 +55,7 @@ type EpubOptions = ConstructorParameters<typeof EPub>[0];
async function generateCollectionEPub(
collection: RawCollectionInfo & Pick<CollectionInfo, "coverImgMeta">,
collectionPosts: ExtendedPostInfo[],
fileLocation: string
fileLocation: string,
) {
const authors = collection.authors.map((id) => {
return unicorns.find((u) => u.id === id).name;
@@ -115,10 +115,10 @@ async function generateCollectionEPub(
collectionPosts.map(async (post) => ({
title: post.title,
data: await generateEpubHTML(post.slug, post.contentMeta),
}))
})),
),
} as Partial<EpubOptions> as EpubOptions,
fileLocation
fileLocation,
);
await epub.render();
@@ -128,12 +128,12 @@ const posts = [...getAllExtendedPosts("en")];
for (const collection of collections) {
const collectionPosts = posts.filter(
(post) => post.collection === collection.slug
(post) => post.collection === collection.slug,
);
generateCollectionEPub(
collection,
collectionPosts,
resolve(process.cwd(), `public/${collection.slug}.epub`)
resolve(process.cwd(), `public/${collection.slug}.epub`),
);
}

View File

@@ -16,16 +16,14 @@ export const createIndex = async () => {
{
name: "authorName",
getFn: (post) => {
return (post as any).authorsMeta
.map((author) => author.name)
.join(", ");
return post.authorsMeta.map((author) => author.name).join(", ");
},
weight: 1.8,
},
{
name: "authorHandles",
getFn: (post) => {
return (post as any).authorsMeta
return post.authorsMeta
.flatMap((author) => Object.values(author.socials))
.join(", ");
},
@@ -35,7 +33,7 @@ export const createIndex = async () => {
{ name: "description", weight: 1.2 },
{ name: "excerpt", weight: 1.2 },
],
posts
posts,
).toJSON();
};

View File

@@ -71,7 +71,7 @@ const page = await context.newPage();
async function renderPostImage(
layout: Layout,
post: ExtendedPostInfo,
path: string
path: string,
) {
const label = `${post.slug} (${layout.name})`;
console.time(label);
@@ -97,7 +97,7 @@ for (const post of getAllExtendedPosts("en")) {
await renderPostImage(
twitterPreview,
post,
resolve(outDir, `.${post.socialImg}`)
resolve(outDir, `.${post.socialImg}`),
);
}
}

View File

@@ -1,18 +1,20 @@
import * as React from 'preact';
import { readFileAsBase64 } from '../utils';
import { ComponentProps, Layout } from '../base';
import style from './banner-css';
import classnames from 'classnames';
import * as React from "preact";
import { readFileAsBase64 } from "../utils";
import { ComponentProps, Layout } from "../base";
import style from "./banner-css";
import classnames from "classnames";
const tagStickers: Record<string, string> = {
"default": readFileAsBase64("public/stickers/role_devops.png"),
default: readFileAsBase64("public/stickers/role_devops.png"),
"html,webdev": readFileAsBase64("public/stickers/html.png"),
"vue": readFileAsBase64("public/stickers/vue.png"),
vue: readFileAsBase64("public/stickers/vue.png"),
"documentation,opinion": readFileAsBase64("public/stickers/role_author.png"),
'computer science,bash,javascript': readFileAsBase64("public/stickers/role_developer.png"),
"design": readFileAsBase64("public/stickers/role_designer.png"),
"rust": readFileAsBase64("public/stickers/ferris.png"),
"git": readFileAsBase64("public/stickers/git.png"),
"computer science,bash,javascript": readFileAsBase64(
"public/stickers/role_developer.png",
),
design: readFileAsBase64("public/stickers/role_designer.png"),
rust: readFileAsBase64("public/stickers/ferris.png"),
git: readFileAsBase64("public/stickers/git.png"),
};
function BannerCodeScreen({
@@ -20,16 +22,18 @@ function BannerCodeScreen({
postHtml,
blur,
}: {
post: ComponentProps['post'],
postHtml: string,
blur?: boolean,
post: ComponentProps["post"];
postHtml: string;
blur?: boolean;
}) {
const rotX = (post.description.length % 20) - 10;
const rotY = ((post.title.length * 3) % 20);
const rotY = (post.title.length * 3) % 20;
let tagImg = tagStickers["default"];
for (const tag of post.tags) {
const key = Object.keys(tagStickers).find(k => k.split(",").includes(tag));
const key = Object.keys(tagStickers).find((k) =>
k.split(",").includes(tag),
);
if (key) {
tagImg = tagStickers[key];
break;
@@ -38,38 +42,45 @@ function BannerCodeScreen({
const theme = post.title.length % 3;
return <>
<div class={classnames("absoluteFill", "codeScreenBg", blur && "blur", "theme-" + theme)} style={`--rotX: ${rotX}deg; --rotY: ${rotY}deg; --left: ${rotY}%;`}>
<div class="codeScreen">
<pre dangerouslySetInnerHTML={{ __html: postHtml }} />
<div class="tags">
{
post.tags.map((tag) => (
return (
<>
<div
class={classnames(
"absoluteFill",
"codeScreenBg",
blur && "blur",
"theme-" + theme,
)}
style={`--rotX: ${rotX}deg; --rotY: ${rotY}deg; --left: ${rotY}%;`}
>
<div class="codeScreen">
<pre dangerouslySetInnerHTML={{ __html: postHtml }} />
<div class="tags">
{post.tags.map((tag) => (
<span key={tag}>{tag}</span>
))
}
))}
</div>
</div>
<div class="rect" style="--z: 60px; --x: -80px; --y: -150px;">
<img src={tagImg} />
</div>
</div>
<div class="rect" style="--z: 60px; --x: -80px; --y: -150px;">
<img src={tagImg} />
</div>
</div>
</>;
</>
);
}
function Banner({
post,
postHtml,
}: ComponentProps) {
return <>
<BannerCodeScreen post={post} postHtml={postHtml} />
<div
className="absoluteFill codeScreenOverlay"
style={{
zIndex: -1,
}}
/>
</>;
function Banner({ post, postHtml }: ComponentProps) {
return (
<>
<BannerCodeScreen post={post} postHtml={postHtml} />
<div
className="absoluteFill codeScreenOverlay"
style={{
zIndex: -1,
}}
/>
</>
);
}
export default {

View File

@@ -1,54 +1,54 @@
import * as React from 'preact';
import * as React from "preact";
import { render } from "@testing-library/preact";
import { MockPost } from "__mocks__/data/mock-post";
import TwitterLargeCard, { splitSentence } from "./twitter-preview";
test("Social previews splitSentence", () => {
// doesn't split at start/end of short titles
expect(splitSentence("Topic: Topic")).toStrictEqual(["Topic: Topic", ""]);
// doesn't split at start/end of short titles
expect(splitSentence("Topic: Topic")).toStrictEqual(["Topic: Topic", ""]);
// splits by colon (including the colon char)
expect(splitSentence("A Topic: an Attribute")).toStrictEqual([
"A Topic",
": an Attribute",
]);
// splits by colon (including the colon char)
expect(splitSentence("A Topic: an Attribute")).toStrictEqual([
"A Topic",
": an Attribute",
]);
// splits by commas
expect(
splitSentence("An Attribute of Topic, Topic, and Topic")
).toStrictEqual(["An Attribute of ", "Topic, Topic, and Topic"]);
// splits by commas
expect(
splitSentence("An Attribute of Topic, Topic, and Topic"),
).toStrictEqual(["An Attribute of ", "Topic, Topic, and Topic"]);
// splits by apostrophe
expect(splitSentence("A Topic's Attribute")).toStrictEqual([
"A Topic's",
" Attribute",
]);
// splits by apostrophe
expect(splitSentence("A Topic's Attribute")).toStrictEqual([
"A Topic's",
" Attribute",
]);
// splits by apostrophe (plural)
expect(splitSentence("Some Topics' Attributes")).toStrictEqual([
"Some Topics'",
" Attributes",
]);
// splits by apostrophe (plural)
expect(splitSentence("Some Topics' Attributes")).toStrictEqual([
"Some Topics'",
" Attributes",
]);
// splits by lowercase words
expect(splitSentence("An Attribute in a Topic")).toStrictEqual([
"An Attribute in ",
"a Topic",
]);
// splits by lowercase words
expect(splitSentence("An Attribute in a Topic")).toStrictEqual([
"An Attribute in ",
"a Topic",
]);
});
test("Social preview renders", async () => {
const post = MockPost;
const { baseElement, findByText } = render(
<TwitterLargeCard.Component
post={post}
postHtml="<code>test();</code>"
width={1280}
height={640}
authorImageMap={{ [post.authors[0]]: "test.jpg" }}
/>
);
const post = MockPost;
const { baseElement, findByText } = render(
<TwitterLargeCard.Component
post={post}
postHtml="<code>test();</code>"
width={1280}
height={640}
authorImageMap={{ [post.authors[0]]: "test.jpg" }}
/>,
);
expect(baseElement).toBeInTheDocument();
expect(await findByText(post.title)).toBeInTheDocument();
expect(baseElement).toBeInTheDocument();
expect(await findByText(post.title)).toBeInTheDocument();
});

View File

@@ -1,130 +1,134 @@
import * as React from 'preact';
import { readFileAsBase64 } from '../utils';
import { ComponentProps, Layout } from '../base';
import style from './twitter-preview-css';
import * as React from "preact";
import { readFileAsBase64 } from "../utils";
import { ComponentProps, Layout } from "../base";
import style from "./twitter-preview-css";
export function splitSentence(str: string): [string, string] {
const splitStr = str.split(" ");
const splitBy = (
regex: RegExp,
matchLast: boolean = true
): [string, string] | null => {
const matches = splitStr.map((word, i) => ({ reg: regex.exec(word), i }));
const match = (matchLast ? matches.reverse() : matches)
.slice(1, -1)
.find(({ reg }) => !!reg);
const splitStr = str.split(" ");
const splitBy = (
regex: RegExp,
matchLast: boolean = true,
): [string, string] | null => {
const matches = splitStr.map((word, i) => ({ reg: regex.exec(word), i }));
const match = (matchLast ? matches.reverse() : matches)
.slice(1, -1)
.find(({ reg }) => !!reg);
// if match is not found, fail
if (!match || !match.reg) return null;
// if match is not found, fail
if (!match || !match.reg) return null;
const firstHalf = [
...splitStr.slice(0, match.i),
match.reg.input.substring(0, match.reg.index),
].join(" ");
const secondHalf = [match.reg[0], ...splitStr.slice(match.i + 1)].join(" ");
return [firstHalf, secondHalf];
};
const firstHalf = [
...splitStr.slice(0, match.i),
match.reg.input.substring(0, match.reg.index),
].join(" ");
const secondHalf = [match.reg[0], ...splitStr.slice(match.i + 1)].join(" ");
return [firstHalf, secondHalf];
};
let ret;
// try to split by "Topic[: Attribute]" or "Topic [- Attribute]" (hyphens/colons)
if ((ret = splitBy(/(?<=^\w+):$|^[-—]$/))) return ret;
// try to split by "Attribute in [Topic, Topic, and Topic]" (commas)
if ((ret = splitBy(/^\w+,$/, false))) return ret;
// try to split by "Topic['s Attribute]" (apostrophe)
if ((ret = splitBy(/(?<=^\w+\'s?)$/))) return ret;
// try to split by "Attribute [in Topic]" (lowercase words)
if ((ret = splitBy(/^[a-z][A-Za-z]*$/))) return ret;
// otherwise, don't split the string
return [str, ""];
let ret;
// try to split by "Topic[: Attribute]" or "Topic [- Attribute]" (hyphens/colons)
if ((ret = splitBy(/(?<=^\w+):$|^[-—]$/))) return ret;
// try to split by "Attribute in [Topic, Topic, and Topic]" (commas)
if ((ret = splitBy(/^\w+,$/, false))) return ret;
// try to split by "Topic['s Attribute]" (apostrophe)
if ((ret = splitBy(/(?<=^\w+\'s?)$/))) return ret;
// try to split by "Attribute [in Topic]" (lowercase words)
if ((ret = splitBy(/^[a-z][A-Za-z]*$/))) return ret;
// otherwise, don't split the string
return [str, ""];
}
const unicornUtterancesHead = readFileAsBase64("src/assets/unicorn_head_1024.png");
const unicornUtterancesHead = readFileAsBase64(
"src/assets/unicorn_head_1024.png",
);
interface TwitterCodeScreenProps {
title: string;
html: string;
blur: boolean;
title: string;
html: string;
blur: boolean;
}
const TwitterCodeScreen = ({ title, html, blur }: TwitterCodeScreenProps) => {
const rotations = [
"rotateX(-17deg) rotateY(32deg) rotateZ(-3deg) translate(16%, 0%)",
"rotateX(5deg) rotateY(35deg) rotateZ(345deg) translate(18%, 0)",
"rotateX(15deg) rotateY(25deg) rotateZ(12deg) translate(3%, -15%)",
];
const rotations = [
"rotateX(-17deg) rotateY(32deg) rotateZ(-3deg) translate(16%, 0%)",
"rotateX(5deg) rotateY(35deg) rotateZ(345deg) translate(18%, 0)",
"rotateX(15deg) rotateY(25deg) rotateZ(12deg) translate(3%, -15%)",
];
// use second char of title as "deterministic" random value
const transform = rotations[title.charCodeAt(1) % rotations.length];
// use second char of title as "deterministic" random value
const transform = rotations[title.charCodeAt(1) % rotations.length];
return (
<div className={`absoluteFill codeScreenBg ${blur ? "blur" : ""}`}>
<div
className="absoluteFill codeScreen"
style={`transform: ${transform};`}
>
<div className="absoluteFill">
<pre dangerouslySetInnerHTML={{ __html: html }} />
</div>
</div>
</div>
);
return (
<div className={`absoluteFill codeScreenBg ${blur ? "blur" : ""}`}>
<div
className="absoluteFill codeScreen"
style={`transform: ${transform};`}
>
<div className="absoluteFill">
<pre dangerouslySetInnerHTML={{ __html: html }} />
</div>
</div>
</div>
);
};
const TwitterLargeCard = ({
post,
postHtml,
width,
authorImageMap,
post,
postHtml,
width,
authorImageMap,
}: ComponentProps) => {
const title = post.title;
const [firstHalfTitle, secondHalfTitle] = splitSentence(title);
const title = post.title;
const [firstHalfTitle, secondHalfTitle] = splitSentence(title);
return <>
<TwitterCodeScreen title={post.title} html={postHtml} blur={true} />
<TwitterCodeScreen title={post.title} html={postHtml} blur={false} />
<div className="absoluteFill codeScreenOverlay" />
<div className="absoluteFill centerAll">
<h1
style={{
maxWidth: "90%",
textAlign: "center",
fontSize: `clamp(300%, 4.5rem, ${
Math.round(width / title.length) * 3
}px)`,
}}
>
{firstHalfTitle}
<span className="secondHalfTitle">{secondHalfTitle}</span>
</h1>
</div>
<div
className="absoluteFill backgroundColor"
style={{
zIndex: -1,
}}
/>
<div className="bottomContainer">
<div className="bottomImagesContainer centerAll">
{post.authors.map((author) => (
<img
key={author}
src={authorImageMap[author]}
alt=""
className="bottomProfImg"
height={80}
width={80}
/>
))}
</div>
<div className="bottomImagesContainer centerAll">
<p>unicorn-utterances.com</p>
<img src={unicornUtterancesHead} alt="" height={80} width={80} />
</div>
</div>
</>;
return (
<>
<TwitterCodeScreen title={post.title} html={postHtml} blur={true} />
<TwitterCodeScreen title={post.title} html={postHtml} blur={false} />
<div className="absoluteFill codeScreenOverlay" />
<div className="absoluteFill centerAll">
<h1
style={{
maxWidth: "90%",
textAlign: "center",
fontSize: `clamp(300%, 4.5rem, ${
Math.round(width / title.length) * 3
}px)`,
}}
>
{firstHalfTitle}
<span className="secondHalfTitle">{secondHalfTitle}</span>
</h1>
</div>
<div
className="absoluteFill backgroundColor"
style={{
zIndex: -1,
}}
/>
<div className="bottomContainer">
<div className="bottomImagesContainer centerAll">
{post.authors.map((author) => (
<img
key={author}
src={authorImageMap[author]}
alt=""
className="bottomProfImg"
height={80}
width={80}
/>
))}
</div>
<div className="bottomImagesContainer centerAll">
<p>unicorn-utterances.com</p>
<img src={unicornUtterancesHead} alt="" height={80} width={80} />
</div>
</div>
</>
);
};
export default {
name: "twitter-preview",
css: style,
Component: TwitterLargeCard,
name: "twitter-preview",
css: style,
Component: TwitterLargeCard,
} as Layout;

View File

@@ -4,4 +4,4 @@
"title.where_to_find_us": "Dónde encontrarnos",
"title.sponsors": "Patrocinadores",
"desc.about_us": "Acerca de Unicorn Utterances - quiénes somos, cuáles son nuestros objetivos, y cómo queremos ayudar a que otros aprendan"
}
}

View File

@@ -4,4 +4,4 @@
"title.where_to_find_us": "Où Nous Trouver",
"title.sponsors": "Partenaires",
"desc.about_us": "À propos de Unicorn Utterances - qui nous sommes, quels sont nos buts, et comment nous voulons aidez les gens à apprendre"
}
}

View File

@@ -4,4 +4,4 @@
"title.where_to_find_us": "Onde nos podes encontrar?",
"title.sponsors": "Patrocinadores",
"desc.about_us": "Sobre Unicorn Utterances - quem somos nós, quais são os nossos objetivos e como queremos ajudar no processo de aprendizagem dos outros"
}
}

1393
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -43,26 +43,26 @@
"@astrojs/image": "^0.17.1",
"@astrojs/mdx": "^0.19.7",
"@astrojs/preact": "^2.2.1",
"@astrojs/sitemap": "^1.3.3",
"@astrojs/sitemap": "^2.0.1",
"@lesjoursfr/html-to-epub": "^4.0.0",
"@remark-embedder/core": "^3.0.1",
"@remark-embedder/transformer-oembed": "^3.0.0",
"@testing-library/dom": "^9.3.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/preact": "^3.2.3",
"@types/jest": "^29.4.0",
"@types/node": "^18.13.0",
"@types/node": "^20.4.4",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"@vercel/node": "^2.15.2",
"astro": "^2.7.0",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"@vercel/node": "^2.15.6",
"astro": "^2.9.2",
"astro-icon": "^0.8.1",
"autoprefixer": "^10.4.14",
"classnames": "^2.3.2",
"dayjs": "^1.11.8",
"env-cmd": "^10.1.0",
"esbuild": "^0.18.7",
"esbuild": "^0.18.16",
"esbuild-jest": "^0.5.0",
"eslint": "^8.43.0",
"eslint-plugin-astro": "^0.27.1",
@@ -85,11 +85,11 @@
"lint-staged": "^13.2.2",
"npm-run-all": "^4.1.5",
"playwright": "^1.36.1",
"postcss": "^8.4.24",
"postcss": "^8.4.27",
"postcss-csso": "^6.0.1",
"preact-render-to-string": "^6.1.0",
"prettier": "^2.8.8",
"prettier-plugin-astro": "^0.10.0",
"prettier": "^3.0.0",
"prettier-plugin-astro": "^0.11.0",
"probe-image-size": "^7.2.3",
"rehype-raw": "^6.1.1",
"rehype-retext": "^3.0.2",
@@ -99,17 +99,17 @@
"remark-shiki-twoslash": "^3.1.3",
"remark-unwrap-images": "^3.0.1",
"retext-english": "^4.1.0",
"sass": "^1.63.6",
"sass": "^1.64.1",
"sitemap": "^7.1.1",
"symlink-dir": "^5.1.1",
"terser": "^5.18.1",
"terser": "^5.19.2",
"tsx": "^3.12.7",
"typescript": "^4.9.5",
"typescript": "^5.1.6",
"unified": "^10.1.2",
"unist-util-find-all-after": "^4.0.1",
"unist-util-is": "^5.2.1",
"unist-util-find-all-after": "^5.0.0",
"unist-util-is": "^6.0.0",
"unist-util-replace-all-between": "^0.1.1",
"unist-util-visit": "^4.1.2",
"unist-util-visit": "^5.0.0",
"vite-plugin-svgr": "^3.2.0"
},
"lint-staged": {
@@ -117,10 +117,10 @@
"*.{js,ts,astro,jsx}": "prettier --write"
},
"dependencies": {
"react-aria": "^3.26.0",
"react-stately": "^3.24.0",
"medium-zoom": "^1.0.8",
"preact": "^10.16.0"
"preact": "^10.16.0",
"react-aria": "^3.26.0",
"react-stately": "^3.24.0"
},
"overrides": {
"react": "npm:@preact/compat@latest",

View File

@@ -1,7 +1,4 @@
// @ts-nocheck
module.exports = {
plugins: [
require("autoprefixer"),
require("postcss-csso"),
],
plugins: [require("autoprefixer"), require("postcss-csso")],
};

View File

@@ -12,7 +12,7 @@ export type StickerInfo = {
export async function createSticker(
name: string,
width: number = STICKER_SIZE,
height: number = STICKER_SIZE
height: number = STICKER_SIZE,
): Promise<StickerInfo> {
const image = await getImage({
src: `/stickers/${name}.png`,

View File

@@ -33,7 +33,7 @@ const ButtonWrapper = forwardRef(
rightIcon,
...props
}: ButtonProps<T>,
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>,
) => {
const Wrapper: any = tag;
@@ -49,13 +49,13 @@ const ButtonWrapper = forwardRef(
{rightIcon && <div class="buttonIcon">{rightIcon}</div>}
</Wrapper>
);
}
},
);
export const Button = forwardRef(
<T extends AllowedTags = "a">(
{ class: className = "", ...props }: ButtonProps<T>,
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>,
) => {
return (
<ButtonWrapper
@@ -64,13 +64,13 @@ export const Button = forwardRef(
ref={ref}
/>
);
}
},
);
export const LargeButton = forwardRef(
<T extends AllowedTags = "a">(
{ class: className = "", ...props }: ButtonProps<T>,
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>,
) => {
return (
<ButtonWrapper
@@ -79,7 +79,7 @@ export const LargeButton = forwardRef(
ref={ref}
/>
);
}
},
);
type IconOnlyButtonProps<T extends AllowedTags = "a"> = Omit<
@@ -90,7 +90,7 @@ type IconOnlyButtonProps<T extends AllowedTags = "a"> = Omit<
export const IconOnlyButton = forwardRef(
<T extends AllowedTags = "a">(
{ class: className = "", children, ...props }: IconOnlyButtonProps<T>,
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>,
) => {
return (
<ButtonWrapper
@@ -101,18 +101,18 @@ export const IconOnlyButton = forwardRef(
<div class="iconOnlyButtonIcon">{children}</div>
</ButtonWrapper>
);
}
},
);
export const LargeIconOnlyButton = forwardRef(
<T extends AllowedTags = "a">(
{ class: className = "", children, ...props }: IconOnlyButtonProps<T>,
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>
ref: Ref<T extends "a" ? HTMLAnchorElement : HTMLButtonElement>,
) => {
return (
<ButtonWrapper {...props} class={`iconOnly large ${className}`} ref={ref}>
<div class="iconOnlyButtonIcon">{children}</div>
</ButtonWrapper>
);
}
},
);

View File

@@ -54,7 +54,8 @@
outline-offset: 0;
border-color: transparent;
@include transition(color background-color box-shadow);
outline: var(--chip_focus-outline_color) solid var(--chip_focus-outline_width);
outline: var(--chip_focus-outline_color) solid
var(--chip_focus-outline_width);
color: var(--chip_foreground-color);
}

View File

@@ -1,4 +1,4 @@
import {Children} from 'react';
import { Children } from "react";
import { PropsWithChildren } from "../types";
import style from "./chip.module.scss";

View File

@@ -1,7 +1,6 @@
@import "src/tokens/index";
:root {
--collection-card_corner-radius: var(--corner-radius_xl);
--collection-card_author_padding-top: var(--spc-3x);

View File

@@ -1,73 +1,73 @@
import { useLayoutEffect, useState } from "preact/hooks";
import { Pagination } from "components/pagination/pagination";
import { PostCard } from "components/post-card/post-card";
import { PostInfo } from "types/index";
import { ProfilePictureMap } from "utils/get-unicorn-profile-pic-map";
interface SearchBarHandlerProps {
posts: PostInfo[];
unicornProfilePicMap: ProfilePictureMap;
totalPosts: number;
}
const PAGE_SIZE = 8;
export const SEARCH_PAGE_KEY = "searchPage";
export const getPageFromQueryParams = () => {
// This will always run on the client
const searchParams = new URLSearchParams(location.search);
const pageStr = searchParams.get(SEARCH_PAGE_KEY);
return pageStr ? Number(pageStr) : null;
};
export const SearchBarHandler = ({
posts,
unicornProfilePicMap,
totalPosts,
}: SearchBarHandlerProps) => {
// Replace this initial value with parsing the QS of the browser and getting `searchPage`
const [page, setPage] = useState(getPageFromQueryParams() ?? 1);
useLayoutEffect(() => {
let previousUrl = "";
const observer = new MutationObserver(function (mutations) {
if (window.location.href !== previousUrl) {
previousUrl = window.location.href;
setPage(getPageFromQueryParams() ?? 1);
}
});
const config = { subtree: true, childList: true };
observer.observe(document, config);
return () => observer.disconnect();
}, []);
const currentPosts = posts.slice(
PAGE_SIZE * (page - 1),
PAGE_SIZE * (page - 1) + PAGE_SIZE
);
return (
<>
<ul data-isPostList="true">
{currentPosts.map((post) => (
<PostCard unicornProfilePicMap={unicornProfilePicMap} post={post} />
))}
</ul>
<Pagination
id=""
rootURL={""}
getPageHref={(pageNum: number) => {
const url = new URL(window.location as never);
url.searchParams.set(SEARCH_PAGE_KEY, "" + pageNum);
return url.href;
}}
page={{
currentPage: page,
lastPage: Math.ceil(totalPosts / PAGE_SIZE),
}}
/>
</>
);
};
import { useLayoutEffect, useState } from "preact/hooks";
import { Pagination } from "components/pagination/pagination";
import { PostCard } from "components/post-card/post-card";
import { PostInfo } from "types/index";
import { ProfilePictureMap } from "utils/get-unicorn-profile-pic-map";
interface SearchBarHandlerProps {
posts: PostInfo[];
unicornProfilePicMap: ProfilePictureMap;
totalPosts: number;
}
const PAGE_SIZE = 8;
export const SEARCH_PAGE_KEY = "searchPage";
export const getPageFromQueryParams = () => {
// This will always run on the client
const searchParams = new URLSearchParams(location.search);
const pageStr = searchParams.get(SEARCH_PAGE_KEY);
return pageStr ? Number(pageStr) : null;
};
export const SearchBarHandler = ({
posts,
unicornProfilePicMap,
totalPosts,
}: SearchBarHandlerProps) => {
// Replace this initial value with parsing the QS of the browser and getting `searchPage`
const [page, setPage] = useState(getPageFromQueryParams() ?? 1);
useLayoutEffect(() => {
let previousUrl = "";
const observer = new MutationObserver(function (mutations) {
if (window.location.href !== previousUrl) {
previousUrl = window.location.href;
setPage(getPageFromQueryParams() ?? 1);
}
});
const config = { subtree: true, childList: true };
observer.observe(document, config);
return () => observer.disconnect();
}, []);
const currentPosts = posts.slice(
PAGE_SIZE * (page - 1),
PAGE_SIZE * (page - 1) + PAGE_SIZE,
);
return (
<>
<ul data-isPostList="true">
{currentPosts.map((post) => (
<PostCard unicornProfilePicMap={unicornProfilePicMap} post={post} />
))}
</ul>
<Pagination
id=""
rootURL={""}
getPageHref={(pageNum: number) => {
const url = new URL(window.location as never);
url.searchParams.set(SEARCH_PAGE_KEY, "" + pageNum);
return url.href;
}}
page={{
currentPage: page,
lastPage: Math.ceil(totalPosts / PAGE_SIZE),
}}
/>
</>
);
};

View File

@@ -64,4 +64,4 @@
flex-shrink: 0;
height: var(--filterBarIconSize);
width: var(--filterBarIconSize);
}
};

View File

@@ -1,48 +1,48 @@
@import "src/tokens/index";
.header {
align-items: center;
position: relative;
}
.buttons {
gap: var(--spc-4x) var(--spc-6x);
padding: var(--spc-4x) 0;
}
.bannerLogo {
display: none;
}
.bannerLogoMobile {
display: block;
position: absolute;
top: calc(-1 * var(--site-spacing));
left: calc(-1 * var(--site-spacing));
right: calc(-1 * var(--site-spacing));
z-index: -1;
}
.bannerTextShadow {
text-shadow:
0 0 5px var(--background_primary),
0 0 6px var(--background_primary),
0 0 8px var(--background_primary),
0 0 8px var(--background_primary);
}
@include from($desktopSmall) {
.header {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--site-spacing);
}
.bannerLogo {
display: block;
}
.bannerLogoMobile {
display: none;
}
}
@import "src/tokens/index";
.header {
align-items: center;
position: relative;
}
.buttons {
gap: var(--spc-4x) var(--spc-6x);
padding: var(--spc-4x) 0;
}
.bannerLogo {
display: none;
}
.bannerLogoMobile {
display: block;
position: absolute;
top: calc(-1 * var(--site-spacing));
left: calc(-1 * var(--site-spacing));
right: calc(-1 * var(--site-spacing));
z-index: -1;
}
.bannerTextShadow {
text-shadow:
0 0 5px var(--background_primary),
0 0 6px var(--background_primary),
0 0 8px var(--background_primary),
0 0 8px var(--background_primary);
}
@include from($desktopSmall) {
.header {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: var(--site-spacing);
}
.bannerLogo {
display: block;
}
.bannerLogoMobile {
display: none;
}
}

View File

@@ -3,26 +3,23 @@ import { JSX } from "preact";
interface PictureProps {
picture: GetPictureResult;
alt: string,
alt: string;
class?: string;
imgAttrs?: JSX.HTMLAttributes<HTMLImageElement>;
}
export const Picture = ({
picture, alt,
picture,
alt,
class: className,
imgAttrs,
}: PictureProps) => {
return (
<picture class={`${className || ''}`}>
<picture class={`${className || ""}`}>
{picture.sources.map((attrs) => (
<source {...attrs} />
))}
<img
{...(picture.image as any)}
{...imgAttrs}
alt={alt}
/>
<img {...(picture.image as any)} {...imgAttrs} alt={alt} />
</picture>
);
};

View File

@@ -38,7 +38,9 @@
border-radius: 8px;
background: var(--form-field_background);
color: var(--foreground_emphasis-high);
margin: calc(var(--form-field_outline_width_focused) - var(--form-field_outline_width));
margin: calc(
var(--form-field_outline_width_focused) - var(--form-field_outline_width)
);
}
.input:hover {
@@ -152,7 +154,8 @@
position: absolute;
}
.closeButtonContainer, .closeButtonContainer > svg {
.closeButtonContainer,
.closeButtonContainer > svg {
height: 100%;
width: 100%;
}

View File

@@ -1,203 +1,230 @@
@import "src/tokens/index";
:root {
--page-card_container_corner-radius: calc(var(--site-spacing) * 2);
--page-card_container_padding: calc(var(--site-spacing) * 2);
--page-card_container_compound-padding: calc(var(--page-card_image_border_width) + var(--page-card_container_padding));
--page-card_description_padding-top: var(--spc-4x);
--page-card_image_size: 240px;
--page-card_image_border_width: 16px;
--page-card_image_inset: var(--spc-12x);
--page_card_image_corner-radius: 48px;
--page-card_button-gap: var(--site-spacing);
--page-card_image_border_color: var(--background_primary);
--page-card_container_background-color: var(--surface_primary_emphasis-none);
--page-card_title_color: var(--foreground_emphasis-high);
--page-card_supporting-text_color: var(--foreground_emphasis-medium);
@include from($tabletSmall) {
--page-card_container_padding: var(--spc-6x);
--page-card_image_size: 240px;
--page-card_max-width: 640px;
}
@include from($tabletLarge) {
--page-card_container_padding: var(--spc-12x);
--page-card_image_size: 320px;
--page-card_max-width: 960px;
}
@include from($desktopSmall) {
--page-card_container_padding: var(--spc-12x);
--page-card_image_inset: var(--spc-16x);
--page-card_image_size: 360px;
--page-card_max-width: 960px;
}
}
.pageCardContainer {
background: var(--page-card_container_background-color);
display: grid;
border-radius: var(--page-card_container_corner-radius);
grid-template-columns: 1fr;
grid-row-gap: var(--page-card_container_padding);
padding: 0 var(--page-card_container_padding) var(--page-card_container_padding) var(--page-card_container_padding);
margin-top: var(--page-card_image_inset);
@include from($tabletSmall) {
max-width: var(--page-card_max-width);
grid-template-columns: auto 1fr;
grid-template-rows: auto auto auto;
grid-row-gap: var(--page-card_container_padding);
grid-column-gap: var(--page-card_container_padding);
padding: var(--page-card_container_padding) var(--page-card_container_compound-padding) var(--page-card_container_compound-padding) var(--page-card_container_compound-padding);
}
@include from($tabletLarge) {
grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr var(--page-card_container_padding) auto;
grid-row-gap: unset;
grid-column-gap: var(--page-card_container_padding);
padding: var(--page-card_container_padding) var(--page-card_container_compound-padding);
}
@include from($desktopSmall) {
margin-top: unset;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto 1fr auto;
grid-column-gap: var(--page-card_container_padding);
grid-row-gap: var(--page-card_description_padding-top);
padding: var(--page-card_container_padding) var(--page-card_container_compound-padding) var(--page-card_container_padding) 0;
}
}
.imageContainer {
display: inline-block;
margin-left: auto;
margin-right: auto;
margin-top: calc(0px - var(--page-card_image_inset));
@include from($tabletSmall) {
margin-left: unset;
margin-right: unset;
grid-row: 1/1;
grid-column: 1/1;
margin-top: calc(0px - var(--page-card_image_inset) - var(--page-card_container_padding));
}
@include from($tabletLarge) {
grid-row: 1/3;
grid-column: 1/1;
margin-top: calc(0px - var(--page-card_image_inset) - var(--page-card_container_padding));
}
@include from($desktopSmall) {
grid-row: 1/5;
grid-column: unset;
margin-top: unset;
margin-left: calc(0px - var(--page-card_image_inset));
}
}
.image {
height: var(--page-card_image_size);
width: var(--page-card_image_size);
border-style: solid;
border-radius: var(--page_card_image_corner-radius);
border-width: var(--page-card_image_border_width);
border-color: var(--page-card_image_border_color);
overflow: hidden;
}
.title {
margin: 0;
color: var(--foreground_emphasis-high);
@include from($tabletSmall) {
display: flex;
align-items: center;
padding-bottom: var(--page-card_container_padding);
}
@include from($tabletLarge) {
display: flex;
align-items: flex-end;
padding-bottom: unset;
margin-bottom: var(--page-card_description_padding-top);
}
@include from($desktopSmall) {
display: unset;
align-items: unset;
margin-bottom: unset;
margin-top: calc(var(--page-card_container_compound-padding) - var(--page-card_container_padding));
}
}
.description {
margin: 0;
color: var(--foreground_emphasis-medium);
@include from($tabletSmall) {
grid-column: 1/3;
padding: 0 calc(var(--page-card_container_compound-padding) - var(--page-card_container_padding));
}
@include from($tabletLarge) {
grid-column: unset;
padding: unset;
}
}
.pageCardSpacer {
display: none;
@include from($tabletLarge) {
all: unset;
}
}
.buttonsContainer {
display: flex;
flex-direction: column;
gap: var(--page-card_button-gap);
@include from($tabletSmall) {
flex-direction: row;
grid-column: 1/3;
padding: 0 calc(var(--page-card_container_compound-padding) - var(--page-card_container_padding));
}
@include from($desktopSmall) {
grid-column: unset;
justify-content: space-between;
margin-bottom: calc(var(--page-card_container_compound-padding) - var(--page-card_container_padding));
padding: unset;
}
}
.buttonsContainer.twoButtons > * {
@include from($tabletSmall) {
flex-basis: 50%;
}
@include from($desktopSmall) {
flex-basis: unset;
}
}
.buttonsContainer:not(.twoButtons) > * {
@include from($tabletSmall) {
flex-basis: 100%;
}
@include from($desktopSmall) {
flex-basis: unset;
}
}
@import "src/tokens/index";
:root {
--page-card_container_corner-radius: calc(var(--site-spacing) * 2);
--page-card_container_padding: calc(var(--site-spacing) * 2);
--page-card_container_compound-padding: calc(
var(--page-card_image_border_width) + var(--page-card_container_padding)
);
--page-card_description_padding-top: var(--spc-4x);
--page-card_image_size: 240px;
--page-card_image_border_width: 16px;
--page-card_image_inset: var(--spc-12x);
--page_card_image_corner-radius: 48px;
--page-card_button-gap: var(--site-spacing);
--page-card_image_border_color: var(--background_primary);
--page-card_container_background-color: var(--surface_primary_emphasis-none);
--page-card_title_color: var(--foreground_emphasis-high);
--page-card_supporting-text_color: var(--foreground_emphasis-medium);
@include from($tabletSmall) {
--page-card_container_padding: var(--spc-6x);
--page-card_image_size: 240px;
--page-card_max-width: 640px;
}
@include from($tabletLarge) {
--page-card_container_padding: var(--spc-12x);
--page-card_image_size: 320px;
--page-card_max-width: 960px;
}
@include from($desktopSmall) {
--page-card_container_padding: var(--spc-12x);
--page-card_image_inset: var(--spc-16x);
--page-card_image_size: 360px;
--page-card_max-width: 960px;
}
}
.pageCardContainer {
background: var(--page-card_container_background-color);
display: grid;
border-radius: var(--page-card_container_corner-radius);
grid-template-columns: 1fr;
grid-row-gap: var(--page-card_container_padding);
padding: 0 var(--page-card_container_padding)
var(--page-card_container_padding) var(--page-card_container_padding);
margin-top: var(--page-card_image_inset);
@include from($tabletSmall) {
max-width: var(--page-card_max-width);
grid-template-columns: auto 1fr;
grid-template-rows: auto auto auto;
grid-row-gap: var(--page-card_container_padding);
grid-column-gap: var(--page-card_container_padding);
padding: var(--page-card_container_padding)
var(--page-card_container_compound-padding)
var(--page-card_container_compound-padding)
var(--page-card_container_compound-padding);
}
@include from($tabletLarge) {
grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr var(--page-card_container_padding) auto;
grid-row-gap: unset;
grid-column-gap: var(--page-card_container_padding);
padding: var(--page-card_container_padding)
var(--page-card_container_compound-padding);
}
@include from($desktopSmall) {
margin-top: unset;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto 1fr auto;
grid-column-gap: var(--page-card_container_padding);
grid-row-gap: var(--page-card_description_padding-top);
padding: var(--page-card_container_padding)
var(--page-card_container_compound-padding)
var(--page-card_container_padding) 0;
}
}
.imageContainer {
display: inline-block;
margin-left: auto;
margin-right: auto;
margin-top: calc(0px - var(--page-card_image_inset));
@include from($tabletSmall) {
margin-left: unset;
margin-right: unset;
grid-row: 1/1;
grid-column: 1/1;
margin-top: calc(
0px - var(--page-card_image_inset) - var(--page-card_container_padding)
);
}
@include from($tabletLarge) {
grid-row: 1/3;
grid-column: 1/1;
margin-top: calc(
0px - var(--page-card_image_inset) - var(--page-card_container_padding)
);
}
@include from($desktopSmall) {
grid-row: 1/5;
grid-column: unset;
margin-top: unset;
margin-left: calc(0px - var(--page-card_image_inset));
}
}
.image {
height: var(--page-card_image_size);
width: var(--page-card_image_size);
border-style: solid;
border-radius: var(--page_card_image_corner-radius);
border-width: var(--page-card_image_border_width);
border-color: var(--page-card_image_border_color);
overflow: hidden;
}
.title {
margin: 0;
color: var(--foreground_emphasis-high);
@include from($tabletSmall) {
display: flex;
align-items: center;
padding-bottom: var(--page-card_container_padding);
}
@include from($tabletLarge) {
display: flex;
align-items: flex-end;
padding-bottom: unset;
margin-bottom: var(--page-card_description_padding-top);
}
@include from($desktopSmall) {
display: unset;
align-items: unset;
margin-bottom: unset;
margin-top: calc(
var(--page-card_container_compound-padding) -
var(--page-card_container_padding)
);
}
}
.description {
margin: 0;
color: var(--foreground_emphasis-medium);
@include from($tabletSmall) {
grid-column: 1/3;
padding: 0
calc(
var(--page-card_container_compound-padding) -
var(--page-card_container_padding)
);
}
@include from($tabletLarge) {
grid-column: unset;
padding: unset;
}
}
.pageCardSpacer {
display: none;
@include from($tabletLarge) {
all: unset;
}
}
.buttonsContainer {
display: flex;
flex-direction: column;
gap: var(--page-card_button-gap);
@include from($tabletSmall) {
flex-direction: row;
grid-column: 1/3;
padding: 0
calc(
var(--page-card_container_compound-padding) -
var(--page-card_container_padding)
);
}
@include from($desktopSmall) {
grid-column: unset;
justify-content: space-between;
margin-bottom: calc(
var(--page-card_container_compound-padding) -
var(--page-card_container_padding)
);
padding: unset;
}
}
.buttonsContainer.twoButtons > * {
@include from($tabletSmall) {
flex-basis: 50%;
}
@include from($desktopSmall) {
flex-basis: unset;
}
}
.buttonsContainer:not(.twoButtons) > * {
@include from($tabletSmall) {
flex-basis: 100%;
}
@include from($desktopSmall) {
flex-basis: unset;
}
}

View File

@@ -8,7 +8,7 @@ test("when the menu button is clicked, the menu popup is opened", async () => {
currentPage: 2,
lastPage: 11,
}}
/>
/>,
);
// click the "..." menu
@@ -27,7 +27,7 @@ test("when '+' is clicked, the page number is incremented", async () => {
currentPage: 2,
lastPage: 11,
}}
/>
/>,
);
// click the "..." menu
@@ -54,7 +54,7 @@ test("when '-' is clicked, the page number is decremented", async () => {
currentPage: 2,
lastPage: 11,
}}
/>
/>,
);
// click the "..." menu
@@ -84,7 +84,7 @@ test("when 'Go to page' is clicked, softNavigate is invoked with the input page
}}
getPageHref={(pageNum: number) => `./${pageNum}`}
softNavigate={softNavigate}
/>
/>,
);
// click the "..." menu

View File

@@ -21,7 +21,7 @@ import { DOMProps } from "@react-types/shared";
function PopupContents(
props: Pick<PaginationProps, "page" | "getPageHref" | "softNavigate"> & {
close: () => void;
}
},
) {
const [count, setCount] = useState(props.page.currentPage);
return (
@@ -123,7 +123,7 @@ function PaginationPopover({
popoverRef,
triggerRef,
},
state
state,
);
/* Setup dialog */
@@ -167,7 +167,7 @@ function PaginationPopover({
}
export function PaginationMenuAndPopover(
props: Pick<PaginationProps, "page" | "getPageHref" | "softNavigate">
props: Pick<PaginationProps, "page" | "getPageHref" | "softNavigate">,
) {
/* Setup trigger */
const triggerRef = useRef(null);
@@ -175,7 +175,7 @@ export function PaginationMenuAndPopover(
const { triggerProps, overlayProps } = useOverlayTrigger(
{ type: "dialog" },
state,
triggerRef
triggerRef,
);
return (

View File

@@ -1,5 +1,5 @@
import { render } from "@testing-library/preact";
import { Pagination } from './pagination';
import { Pagination } from "./pagination";
test("Pagination renders", () => {
const { baseElement, getByText } = render(
@@ -8,7 +8,7 @@ test("Pagination renders", () => {
currentPage: 3,
lastPage: 8,
}}
/>
/>,
);
expect(baseElement).toBeInTheDocument();
@@ -22,7 +22,7 @@ test("when there is only one page, nothing is rendered", () => {
currentPage: 1,
lastPage: 1,
}}
/>
/>,
);
expect(container.childNodes).toHaveLength(0);
@@ -35,7 +35,7 @@ test("when there is more than one page, the pages are rendered", () => {
currentPage: 1,
lastPage: 2,
}}
/>
/>,
);
expect(container.childNodes).not.toHaveLength(0);
@@ -50,7 +50,7 @@ test("when page 1 is selected, its button has the selected state", () => {
currentPage: 1,
lastPage: 11,
}}
/>
/>,
);
const button1 = getByText("1");
@@ -67,7 +67,7 @@ test("when the previous button is clicked, softNavigate is called for the previo
lastPage: 11,
}}
softNavigate={softNavigate}
/>
/>,
);
const previous = getByTestId("pagination-previous");
@@ -86,7 +86,7 @@ test("when the next button is clicked, softNavigate is called for the next page"
lastPage: 11,
}}
softNavigate={softNavigate}
/>
/>,
);
const next = getByTestId("pagination-next");
@@ -105,7 +105,7 @@ test("when a page button is clicked, softNavigate is called for its page", () =>
lastPage: 11,
}}
softNavigate={softNavigate}
/>
/>,
);
const button5 = getByText("5");

View File

@@ -3,7 +3,10 @@ import forward from "src/icons/arrow_right.svg?raw";
import back from "src/icons/arrow_left.svg?raw";
import { PaginationMenuAndPopover } from "components/pagination/pagination-popover";
import { useEffect, useState } from "preact/hooks";
import { PaginationButtonProps, PaginationProps } from "components/pagination/types";
import {
PaginationButtonProps,
PaginationProps,
} from "components/pagination/types";
import { usePagination } from "./use-pagination";
import { onSoftNavClick } from "./on-click-base";
@@ -14,11 +17,18 @@ function PaginationButton({
selected,
softNavigate,
}: PaginationButtonProps) {
const pageOptionalMin = Math.min(Math.max(1, pageInfo.currentPage - 1), pageInfo.lastPage - 3);
const pageOptionalMin = Math.min(
Math.max(1, pageInfo.currentPage - 1),
pageInfo.lastPage - 3,
);
const isOptional = pageNum < pageOptionalMin || pageNum > pageOptionalMin + 3;
return (
<li className={`${styles.paginationItem} ${isOptional ? styles.paginationItemExtra : ''}`}>
<li
className={`${styles.paginationItem} ${
isOptional ? styles.paginationItemExtra : ""
}`}
>
<a
className={`text-style-body-medium-bold ${styles.paginationButton} ${
selected ? styles.selected : ""
@@ -38,7 +48,7 @@ function PaginationButton({
* This prevents the pagination menu from rendering on SSR, which throws errors
*/
function PaginationMenuWrapper(
props: Pick<PaginationProps, "page" | "getPageHref" | "softNavigate">
props: Pick<PaginationProps, "page" | "getPageHref" | "softNavigate">,
) {
const [shouldRender, setShouldRender] = useState(false);

View File

@@ -16,7 +16,7 @@ export function usePagination(page: PageInfo) {
const firstPageNum = Math.max(
2,
Math.min(page.lastPage - PAGE_BUTTON_COUNT, page.currentPage - 1)
Math.min(page.lastPage - PAGE_BUTTON_COUNT, page.currentPage - 1),
);
const pages = [
@@ -32,7 +32,7 @@ export function usePagination(page: PageInfo) {
page.lastPage,
].filter(
// ensure that displayed pages are within the desired range
(i) => (i === "..." && isDotsEnabled) || (+i > 0 && +i <= page.lastPage)
(i) => (i === "..." && isDotsEnabled) || (+i > 0 && +i <= page.lastPage),
);
return {

View File

@@ -26,9 +26,12 @@ export function PostCardGrid({ postsToDisplay, ...props }: PostGridProps) {
unicornProfilePicMap={props.unicornProfilePicMap}
/>
) : (
<PostCard post={post} unicornProfilePicMap={props.unicornProfilePicMap} />
<PostCard
post={post}
unicornProfilePicMap={props.unicornProfilePicMap}
/>
);
})}
</ul>
)
);
}

View File

@@ -119,7 +119,8 @@
text-decoration: none;
}
.authorName:hover, .authorName:focus {
.authorName:hover,
.authorName:focus {
text-decoration: underline;
}

View File

@@ -8,7 +8,7 @@
gap: var(--spc-1x);
border-radius: var(--corner-radius_m);
text-decoration: none;
@include transition(background-color);
}

View File

@@ -49,15 +49,21 @@
}
.searchButton {
height: calc(var(--p_medium_line-height) + calc(var(--search-form-field_padding-vertical) * 2));
width: calc(var(--p_medium_line-height) + calc(var(--search-form-field_padding-vertical) * 2));
height: calc(
var(--p_medium_line-height) +
calc(var(--search-form-field_padding-vertical) * 2)
);
width: calc(
var(--p_medium_line-height) +
calc(var(--search-form-field_padding-vertical) * 2)
);
flex-shrink: 0;
}
.chips {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: var(--search-section_chip-gap);
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: var(--search-section_chip-gap);
}

View File

@@ -1,21 +1,21 @@
:root {
--subheader_padding-top: calc(var(--site-spacing) * 2);
--subheader_padding-bottom: var(--spc-4x);
--subheader_gap: var(--spc-4x);
--subheader_padding-top: calc(var(--site-spacing) * 2);
--subheader_padding-bottom: var(--spc-4x);
--subheader_gap: var(--spc-4x);
--subheader_label_color: var(--foreground_emphasis-high);
--subheader_label_color: var(--foreground_emphasis-high);
}
.container {
padding-top: var(--subheader_padding-top);
padding-bottom: var(--subheader_padding-bottom);
display: flex;
flex-direction: row;
align-items: center;
gap: var(--subheader_gap);
padding-top: var(--subheader_padding-top);
padding-bottom: var(--subheader_padding-bottom);
display: flex;
flex-direction: row;
align-items: center;
gap: var(--subheader_gap);
}
.heading {
color: var(--subheader_label_color);
margin: 0;
}
color: var(--subheader_label_color);
margin: 0;
}

View File

@@ -3,25 +3,38 @@ import styles from "./subheader.module.scss";
import { createElement } from "preact";
type SubHeaderProps = PropsWithOptionalChildren<{
tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
class?: string;
style?: string;
text: string;
tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
class?: string;
style?: string;
text: string;
}>;
export function SubHeader({ tag, children, text, class: className, ...props }: SubHeaderProps) {
const Heading = (props: any) => createElement(tag, {
...props,
}, props.children);
export function SubHeader({
tag,
children,
text,
class: className,
...props
}: SubHeaderProps) {
const Heading = (props: any) =>
createElement(
tag,
{
...props,
},
props.children,
);
return (
<div className={styles.container}>
<Heading className={[
styles.heading,
"text-style-headline-4",
className
].filter(c => !!c).join(" ")}>{text}</Heading>
{children}
</div>
);
return (
<div className={styles.container}>
<Heading
className={[styles.heading, "text-style-headline-4", className]
.filter((c) => !!c)
.join(" ")}
>
{text}
</Heading>
{children}
</div>
);
}

2
src/env.d.ts vendored
View File

@@ -1 +1 @@
/// <reference types="@astrojs/image/client" />
/// <reference types="@astrojs/image/client" />

View File

@@ -68,7 +68,8 @@ body {
margin: 0;
padding: 0;
color: var(--foreground_emphasis-high);
transition: color $animStyle $animSpeed,
transition:
color $animStyle $animSpeed,
background-color $animStyle $animSpeed;
}
@@ -94,7 +95,8 @@ body {
height: auto;
}
img.circleImg, .circleImg img {
img.circleImg,
.circleImg img {
flex-shrink: 0;
flex-grow: 0;
border-radius: 50%;

View File

@@ -5,7 +5,7 @@ import { ExtendedPostInfo } from "types/index";
const postImportResult = import.meta.glob<MarkdownInstance<ExtendedPostInfo>>(
"../../content/blog/**/*.md",
{ eager: true }
{ eager: true },
);
const posts = Object.values(postImportResult);
@@ -30,7 +30,7 @@ export const get = () => {
.sort((a, b) =>
new Date(b.frontmatter.published) > new Date(a.frontmatter.published)
? 1
: -1
: -1,
)
.forEach((post) => {
const nodeUrl = `${siteUrl}/posts/${post.frontmatter.slug}`;

View File

@@ -8,12 +8,14 @@ blockquote:not([class]) {
position: relative;
margin: var(--site-spacing) 0;
padding: 0;
padding-left: calc(var(--blockquote_border-width) + var(--blockquote_padding-start));
padding-left: calc(
var(--blockquote_border-width) + var(--blockquote_padding-start)
);
color: var(--foreground_emphasis-medium);
&::before {
content: '';
content: "";
display: block;
position: absolute;
left: 0;

View File

@@ -4,7 +4,10 @@
:root {
--code-block_padding: var(--spc-2x);
--code-block_corner-radius: var(--corner-radius_l);
--code-block_inner-corner-radius: calc(var(--code-block_corner-radius) - var(--code-block_padding) - var(--code-block_border_width));
--code-block_inner-corner-radius: calc(
var(--code-block_corner-radius) - var(--code-block_padding) -
var(--code-block_border_width)
);
--code-block_header_gap: var(--spc-4x);
--code-block_header_min-height: var(--min-target-size_xl);
@@ -30,7 +33,9 @@ pre.shiki {
color: var(--code-block_header_title_color);
min-height: calc(var(--code-block_header_min-height) - 2*var(--code-block_padding));
min-height: calc(
var(--code-block_header_min-height) - 2 * var(--code-block_padding)
);
padding: 0 var(--code-block_header_title_margin-horizontal);
display: flex;

View File

@@ -5,13 +5,17 @@
--embed_header_gap: var(--spc-2x);
--embed_corner-radius: var(--corner-radius_l);
--embed-iframe_margin: calc( var(--embed_header_padding) / 2 );
--embed-iframe_corner-radius: calc( var(--embed_corner-radius) - var(--embed-iframe_margin) );
--embed-iframe_margin: calc(var(--embed_header_padding) / 2);
--embed-iframe_corner-radius: calc(
var(--embed_corner-radius) - var(--embed-iframe_margin)
);
--embed-iframe_border-width: var(--border-width_s);
--embed_background-color: var(--surface_primary_emphasis-none);
--embed_favicon_container_size: var(--min-target-size_m);
--embed_favicon_container_background-color: var(--surface_primary_emphasis-low);
--embed_favicon_container_background-color: var(
--surface_primary_emphasis-low
);
--embed_favicon_icon-size: var(--icon-size_regular);
--embed_favicon_icon_color: var(--primary_on-variant);
--embed_iframe_background-color: var(--surface_primary_emphasis-low);
@@ -41,12 +45,16 @@
width: var(--embed_favicon_container_size);
height: var(--embed_favicon_container_size);
flex-shrink: 0;
padding: calc((var(--embed_favicon_container_size) - var(--embed_favicon_icon-size)) / 2);
padding: calc(
(var(--embed_favicon_container_size) - var(--embed_favicon_icon-size)) /
2
);
background-color: var(--embed_favicon_container_background-color);
border-radius: 50%;
& picture, & img {
& picture,
& img {
width: var(--embed_favicon_icon-size);
height: var(--embed_favicon_icon-size);
}
@@ -56,12 +64,12 @@
flex: 1;
min-width: 0;
&> p {
& > p {
@extend .text-style-body-medium-bold;
margin: 0;
}
&> a {
& > a {
@extend .text-style-body-small-bold;
white-space: nowrap;
overflow: hidden;
@@ -89,7 +97,8 @@
&__placeholder {
background-color: var(--embed_iframe_background-color);
border: var(--embed-iframe_border-width) solid var(--embed_iframe_border_color);
border: var(--embed-iframe_border-width) solid
var(--embed_iframe_border_color);
border-radius: var(--embed-iframe_corner-radius);
margin: var(--embed-iframe_margin);
@@ -99,7 +108,7 @@
margin-top: 0;
}
&> .button {
& > .button {
position: absolute;
top: 50%;
left: 50%;
@@ -107,10 +116,11 @@
}
}
&> iframe {
& > iframe {
box-sizing: border-box;
display: block;
border: var(--embed-iframe_border-width) solid var(--embed_iframe_border_color);
border: var(--embed-iframe_border-width) solid
var(--embed_iframe_border_color);
border-radius: var(--embed-iframe_corner-radius);
margin: var(--embed-iframe_margin);
overflow: hidden;

View File

@@ -9,7 +9,9 @@
--hint-container_corner-radius_expanded: var(--corner-radius_m);
// We calculate the content radius off of two tokens so they update accordingly if any of the values is changed
--hint-content_corner-radius: calc(var(--hint-container_corner-radius_expanded) - var(--hint-container_padding));
--hint-content_corner-radius: calc(
var(--hint-container_corner-radius_expanded) - var(--hint-container_padding)
);
--hint-content_background-color: var(--background_primary);
--hint-content_padding: var(--spc-4x);
@@ -20,8 +22,12 @@
--hint-header_gap: var(--spc-2x);
--hint-container_background-color: var(--surface_primary_emphasis-low);
--hint-container_background-color_hovered: var(--surface_primary_emphasis-medium);
--hint-container_background-color_pressed: var(--surface_primary_emphasis-high);
--hint-container_background-color_hovered: var(
--surface_primary_emphasis-medium
);
--hint-container_background-color_pressed: var(
--surface_primary_emphasis-high
);
--hint-container_background-color_focused: var(--background_focus);
--hint-container_foreground-color: var(--foreground_emphasis-high);
@@ -41,7 +47,7 @@
.hint {
margin: var(--site-spacing) 0;
&> .hint__container {
& > .hint__container {
display: inline-block;
overflow: hidden;
max-width: 100%;
@@ -55,9 +61,9 @@
transform: rotateZ(180deg);
}
&> .hint__title {
& > .hint__title {
&::marker {
content: '';
content: "";
}
display: flex;
@@ -74,7 +80,7 @@
outline: none !important;
}
&> .hint__content {
& > .hint__content {
margin: var(--hint-container_padding);
background-color: var(--hint-content_background-color);
border-radius: var(--hint-content_corner-radius);
@@ -82,12 +88,12 @@
@include transition(background-color);
&> *:first-child {
& > *:first-child {
margin-top: 0;
padding-top: 0;
}
&> *:last-child {
& > *:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
@@ -107,7 +113,8 @@
@supports selector(:has(*)) {
&:has(.hint__title:focus-visible) {
background-color: var(--hint-container_background-color_focused);
outline: var(--hint_focus-outline_width) solid var(--hint_focus-outline_color);
outline: var(--hint_focus-outline_width) solid
var(--hint_focus-outline_color);
}
}
@@ -117,7 +124,8 @@
@supports not selector(:has(*)) {
&:focus-within {
background-color: var(--hint-container_background-color_focused);
outline: var(--hint_focus-outline_width) solid var(--hint_focus-outline_color);
outline: var(--hint_focus-outline_width) solid
var(--hint_focus-outline_color);
}
}
}

View File

@@ -8,7 +8,10 @@
// should match public/icons/list-bowtie-*.svg
--list_bowtie_size: 16px;
--list_bowtie_top: calc((var(--list_item_padding-vertical) / 2) + ((var(--p_medium_line-height) - var(--list_bowtie_size)) / 2))
--list_bowtie_top: calc(
(var(--list_item_padding-vertical) / 2) +
((var(--p_medium_line-height) - var(--list_bowtie_size)) / 2)
);
}
.post-body ul:not([class]) {
@@ -17,16 +20,20 @@
padding: calc(var(--list_item_padding-vertical) / 2) 0;
padding-left: var(--list_padding-start);
&> li {
background: url('/icons/list-bowtie-light.svg') no-repeat 0 var(--list_bowtie_top);
& > li {
background: url("/icons/list-bowtie-light.svg") no-repeat 0
var(--list_bowtie_top);
padding: calc(var(--list_item_padding-vertical) / 2) 0;
padding-left: calc(var(--list_bowtie_size) + var(--list_item_padding-start));
padding-left: calc(
var(--list_bowtie_size) + var(--list_item_padding-start)
);
@include darkTheme {
background: url('/icons/list-bowtie-dark.svg') no-repeat 0 var(--list_bowtie_top);
background: url("/icons/list-bowtie-dark.svg") no-repeat 0
var(--list_bowtie_top);
}
&> *:first-child {
& > *:first-child {
margin-top: 0;
}
}

View File

@@ -7,7 +7,9 @@
--table_header_padding: var(--spc-3x);
--table_grid-wrapper_cell-gap: var(--border-width_s);
--table_grid-wrapper_corner-radius: calc(var(--table_corner-radius) - var(--table_padding-horizontal));
--table_grid-wrapper_corner-radius: calc(
var(--table_corner-radius) - var(--table_padding-horizontal)
);
--table_cell_padding-horizontal: var(--spc-3x);
--table_cell_padding-vertical: var(--spc-2x);
@@ -101,7 +103,7 @@ thead {
position: relative;
&:not(:last-child)::after {
content: '';
content: "";
display: block;
position: absolute;
top: 50%;
@@ -121,17 +123,31 @@ tbody {
border-radius: var(--table_grid-wrapper_corner-radius);
td {
padding: var(--table_cell_padding-vertical) var(--table_cell_padding-horizontal);
background: linear-gradient(var(--table_cell_background-color), var(--table_cell_background-color)),
linear-gradient(var(--table_grid-wrapper_background-color), var(--table_grid-wrapper_background-color));
padding: var(--table_cell_padding-vertical)
var(--table_cell_padding-horizontal);
background: linear-gradient(
var(--table_cell_background-color),
var(--table_cell_background-color)
),
linear-gradient(
var(--table_grid-wrapper_background-color),
var(--table_grid-wrapper_background-color)
);
border: var(--table_grid-wrapper_cell-gap) solid var(--table_grid-wrapper_background-color);
border: var(--table_grid-wrapper_cell-gap) solid
var(--table_grid-wrapper_background-color);
}
tr:nth-child(even) {
td {
background: linear-gradient(var(--table_cell-alternate_background-color), var(--table_cell-alternate_background-color)),
linear-gradient(var(--table_grid-wrapper_background-color), var(--table_grid-wrapper_background-color));
background: linear-gradient(
var(--table_cell-alternate_background-color),
var(--table_cell-alternate_background-color)
),
linear-gradient(
var(--table_grid-wrapper_background-color),
var(--table_grid-wrapper_background-color)
);
}
}
@@ -152,6 +168,7 @@ tbody {
}
}
th > code, td > code {
th > code,
td > code {
word-break: break-word;
}

View File

@@ -50,7 +50,8 @@
user-select: none;
}
.tabs__tab[aria-selected="true"], .tabs__tab[aria-selected=""] {
.tabs__tab[aria-selected="true"],
.tabs__tab[aria-selected=""] {
background-color: var(--tab_background-color_selected);
color: var(--tab_foreground-color_selected);
}
@@ -76,7 +77,8 @@
background-color: var(--tabbed-wrapper_background-color);
}
.tabs__tab-panel[aria-hidden="true"], .tabs__tab-panel[aria-hidden=""] {
.tabs__tab-panel[aria-hidden="true"],
.tabs__tab-panel[aria-hidden=""] {
display: none;
}
@@ -111,7 +113,8 @@
grid-row-start: 2;
grid-column: 1 / 3;
&[aria-hidden="true"], &[aria-hidden=""] {
&[aria-hidden="true"],
&[aria-hidden=""] {
display: block;
visibility: hidden;
}

View File

@@ -31,14 +31,14 @@
align-items: center;
gap: var(--tooltip_icon-margin);
&> svg {
& > svg {
padding: var(--icon-size-dense-padding);
width: var(--tooltip_icon-size);
height: var(--tooltip_icon-size);
color: var(--tooltip_icon-color);
}
&> p {
& > p {
@extend .text-style-body-medium-bold;
margin: 0;
}
@@ -48,18 +48,16 @@
padding-left: calc(var(--tooltip_icon-size) + var(--tooltip_icon-margin));
margin-top: var(--tooltip_desc-gap);
&> * {
& > * {
@extend .text-style-body-small;
}
&> *:first-child {
& > *:first-child {
margin-top: 0;
}
&> *:last-child {
& > *:last-child {
margin-bottom: 0;
}
}
}

View File

@@ -57,16 +57,21 @@
color: var(--foreground_emphasis-high);
h1, .text-style-headline-1,
h2, .text-style-headline-2,
h3, .text-style-headline-3,
h4, .text-style-headline-4,
h5, .text-style-headline-5,
h6, .text-style-headline-6 {
h1,
.text-style-headline-1,
h2,
.text-style-headline-2,
h3,
.text-style-headline-3,
h4,
.text-style-headline-4,
h5,
.text-style-headline-5,
h6,
.text-style-headline-6 {
color: var(--foreground_emphasis-high);
}
// Fix autolink-headings anchors positioning
.anchor {
line-height: 1;
@@ -135,7 +140,8 @@
background-color: #eee;
border-radius: 3px;
border: 1px solid #b4b4b4;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
box-shadow:
0 1px 1px rgba(0, 0, 0, 0.2),
0 2px 0 0 rgba(255, 255, 255, 0.7) inset;
color: #333;
display: inline-block;

View File

@@ -1,28 +1,32 @@
@import "src/tokens/index";
@font-face {
font-family: 'Figtree';
src: url('/fonts/Figtree/Figtree-VariableFont_wght.ttf') format("truetype-variations");
font-family: "Figtree";
src: url("/fonts/Figtree/Figtree-VariableFont_wght.ttf")
format("truetype-variations");
font-weight: 1 999;
}
@font-face {
font-family: 'Figtree';
font-family: "Figtree";
font-style: italic;
src: url('/fonts/Figtree/Figtree-Italic-VariableFont_wght.ttf') format("truetype-variations");
src: url("/fonts/Figtree/Figtree-Italic-VariableFont_wght.ttf")
format("truetype-variations");
font-weight: 1 999;
}
@font-face {
font-family: 'Roboto Mono';
src: url('/fonts/Roboto_Mono/RobotoMono-VariableFont_wght.ttf') format("truetype-variations");
font-family: "Roboto Mono";
src: url("/fonts/Roboto_Mono/RobotoMono-VariableFont_wght.ttf")
format("truetype-variations");
font-weight: 1 999;
}
@font-face {
font-family: 'Roboto Mono';
font-family: "Roboto Mono";
font-style: italic;
src: url('/fonts/Roboto_Mono/RobotoMono-Italic-VariableFont_wght.ttf') format("truetype-variations");
src: url("/fonts/Roboto_Mono/RobotoMono-Italic-VariableFont_wght.ttf")
format("truetype-variations");
font-weight: 1 999;
}
@@ -35,49 +39,56 @@
font-family: var(--uu-font-family);
}
h1, .text-style-headline-1 {
h1,
.text-style-headline-1 {
font-family: var(--uu-font-family);
font-size: var(--h1_font-size);
font-weight: var(--weight_black);
line-height: var(--h1_line-height);
}
h2, .text-style-headline-2 {
h2,
.text-style-headline-2 {
font-family: var(--uu-font-family);
font-size: var(--h2_font-size);
font-weight: var(--weight_extrabold);
line-height: var(--h2_line-height);
}
h3, .text-style-headline-3 {
h3,
.text-style-headline-3 {
font-family: var(--uu-font-family);
font-size: var(--h3_font-size);
font-weight: var(--weight_bold);
line-height: var(--h3_line-height);
}
h4, .text-style-headline-4 {
h4,
.text-style-headline-4 {
font-family: var(--uu-font-family);
font-size: var(--h4_font-size);
font-weight: var(--weight_bold);
line-height: var(--h4_line-height);
}
h5, .text-style-headline-5 {
h5,
.text-style-headline-5 {
font-family: var(--uu-font-family);
font-size: var(--h5_font-size);
font-weight: var(--weight_bold);
line-height: var(--h5_line-height);
}
h6, .text-style-headline-6 {
h6,
.text-style-headline-6 {
font-family: var(--uu-font-family);
font-size: var(--h6_font-size);
font-weight: var(--weight_bold);
line-height: var(--h6_line-height);
}
p, .text-style-body-large {
p,
.text-style-body-large {
font-family: var(--uu-font-family);
font-size: var(--p_large_font-size);
font-weight: var(--weight_regular);
@@ -124,7 +135,8 @@ p, .text-style-body-large {
margin: 0;
}
button, .text-style-button-regular {
button,
.text-style-button-regular {
font-family: var(--uu-font-family);
font-size: var(--button_regular_font-size);
font-weight: var(--weight_semibold);
@@ -132,7 +144,9 @@ button, .text-style-button-regular {
margin: 0;
}
pre, code, .text-style-code {
pre,
code,
.text-style-code {
@extend .text-style-body-large;
font-family: var(--uu-font-family-code);
}

View File

@@ -1,7 +1,7 @@
:root {
--border-width_s: 1px;
--border-width_m: 2px;
--border-width_l: 4px;
--border-width_xl: 8px;
--border-width_focus: var(--border-width_l);
}
:root {
--border-width_s: 1px;
--border-width_m: 2px;
--border-width_l: 4px;
--border-width_xl: 8px;
--border-width_focus: var(--border-width_l);
}

View File

@@ -1,71 +1,71 @@
:root {
--white: #FFF;
--black: #000;
--transparent: rgba(255, 255, 255, 0);
--neutral10: rgba(11, 29, 40, 1);
--neutral10_64: rgba(11, 29, 40, 0.64);
--neutral10_48: rgba(11, 29, 40, 0.48);
--neutral10_32: rgba(11, 29, 40, 0.32);
--neutral10_12: rgba(11, 29, 40, 0.12);
--neutral20: rgba(33, 51, 63, 1);
--neutral30: rgba(54, 72, 83, 1);
--neutral40: rgba(79, 97, 109, 1);
--neutral50: rgba(104, 122, 135, 1);
--neutral60: rgba(129, 148, 162, 1);
--neutral70: rgba(154, 174, 188, 1);
--neutral80: rgba(182, 201, 216, 1);
--neutral90: rgba(210, 229, 244, 1);
--neutral95: rgba(229, 244, 255, 1);
--neutral95_64: rgba(229, 244, 255, 0.64);
--neutral95_48: rgba(229, 244, 255, 0.48);
--neutral95_32: rgba(229, 244, 255, 0.32);
--neutral95_12: rgba(229, 244, 255, 0.12);
--primary10: rgba(0, 30, 46, 1);
--primary20: rgba(0, 52, 77, 1);
--primary30: rgba(0, 76, 109, 1);
--primary40: rgba(0, 101, 144, 1);
--primary40_64: rgba(0, 101, 144, 0.64);
--primary40_32: rgba(0, 101, 144, 0.32);
--primary40_16: rgba(0, 101, 144, 0.16);
--primary50: rgba(0, 127, 180, 1);
--primary60: rgba(51, 153, 209, 1);
--primary70: rgba(85, 180, 237, 1);
--primary80: rgba(135, 206, 255, 1);
--primary80_64: rgba(135, 206, 255, 0.64);
--primary80_32: rgba(135, 206, 255, 0.32);
--primary80_16: rgba(135, 206, 255, 0.16);
--primary90: rgba(200, 230, 255, 1);
--primary95: rgba(229, 242, 255, 1);
--secondary10: rgba(56, 0, 55, 1);
--secondary20: rgba(83, 19, 80, 1);
--secondary30: rgba(109, 44, 105, 1);
--secondary40: rgba(140, 69, 133, 1);
--secondary40_64: rgba(140, 69, 133, 0.64);
--secondary40_32: rgba(140, 69, 133, 0.32);
--secondary40_16: rgba(140, 69, 133, 0.16);
--secondary50: rgba(168, 93, 159, 1);
--secondary60: rgba(197, 119, 187, 1);
--secondary70: rgba(224, 143, 213, 1);
--secondary80: rgba(255, 173, 243, 1);
--secondary80_64: rgba(255, 173, 243, 0.64);
--secondary80_32: rgba(255, 173, 243, 0.32);
--secondary80_16: rgba(255, 173, 243, 0.16);
--secondary90: rgba(255, 214, 245, 1);
--secondary95: rgba(255, 235, 247, 1);
--error10: rgba(65, 0, 2, 1);
--error20: rgba(105, 0, 5, 1);
--error30: rgba(147, 0, 10, 1);
--error40: rgba(186, 26, 26, 1);
--error40_64: rgba(186, 26, 26, 0.64);
--error40_32: rgba(186, 26, 26, 0.32);
--error40_16: rgba(186, 26, 26, 0.16);
--error50: rgba(222, 55, 48, 1);
--error60: rgba(255, 84, 73, 1);
--error70: rgba(255, 137, 125, 1);
--error80: rgba(255, 180, 171, 1);
--error80_64: rgba(255, 180, 171, 0.64);
--error80_32: rgba(255, 180, 171, 0.32);
--error80_16: rgba(255, 180, 171, 0.16);
--error90: rgba(255, 218, 214, 1);
--error95: rgba(255, 237, 234, 1);
--white: #fff;
--black: #000;
--transparent: rgba(255, 255, 255, 0);
--neutral10: rgba(11, 29, 40, 1);
--neutral10_64: rgba(11, 29, 40, 0.64);
--neutral10_48: rgba(11, 29, 40, 0.48);
--neutral10_32: rgba(11, 29, 40, 0.32);
--neutral10_12: rgba(11, 29, 40, 0.12);
--neutral20: rgba(33, 51, 63, 1);
--neutral30: rgba(54, 72, 83, 1);
--neutral40: rgba(79, 97, 109, 1);
--neutral50: rgba(104, 122, 135, 1);
--neutral60: rgba(129, 148, 162, 1);
--neutral70: rgba(154, 174, 188, 1);
--neutral80: rgba(182, 201, 216, 1);
--neutral90: rgba(210, 229, 244, 1);
--neutral95: rgba(229, 244, 255, 1);
--neutral95_64: rgba(229, 244, 255, 0.64);
--neutral95_48: rgba(229, 244, 255, 0.48);
--neutral95_32: rgba(229, 244, 255, 0.32);
--neutral95_12: rgba(229, 244, 255, 0.12);
--primary10: rgba(0, 30, 46, 1);
--primary20: rgba(0, 52, 77, 1);
--primary30: rgba(0, 76, 109, 1);
--primary40: rgba(0, 101, 144, 1);
--primary40_64: rgba(0, 101, 144, 0.64);
--primary40_32: rgba(0, 101, 144, 0.32);
--primary40_16: rgba(0, 101, 144, 0.16);
--primary50: rgba(0, 127, 180, 1);
--primary60: rgba(51, 153, 209, 1);
--primary70: rgba(85, 180, 237, 1);
--primary80: rgba(135, 206, 255, 1);
--primary80_64: rgba(135, 206, 255, 0.64);
--primary80_32: rgba(135, 206, 255, 0.32);
--primary80_16: rgba(135, 206, 255, 0.16);
--primary90: rgba(200, 230, 255, 1);
--primary95: rgba(229, 242, 255, 1);
--secondary10: rgba(56, 0, 55, 1);
--secondary20: rgba(83, 19, 80, 1);
--secondary30: rgba(109, 44, 105, 1);
--secondary40: rgba(140, 69, 133, 1);
--secondary40_64: rgba(140, 69, 133, 0.64);
--secondary40_32: rgba(140, 69, 133, 0.32);
--secondary40_16: rgba(140, 69, 133, 0.16);
--secondary50: rgba(168, 93, 159, 1);
--secondary60: rgba(197, 119, 187, 1);
--secondary70: rgba(224, 143, 213, 1);
--secondary80: rgba(255, 173, 243, 1);
--secondary80_64: rgba(255, 173, 243, 0.64);
--secondary80_32: rgba(255, 173, 243, 0.32);
--secondary80_16: rgba(255, 173, 243, 0.16);
--secondary90: rgba(255, 214, 245, 1);
--secondary95: rgba(255, 235, 247, 1);
--error10: rgba(65, 0, 2, 1);
--error20: rgba(105, 0, 5, 1);
--error30: rgba(147, 0, 10, 1);
--error40: rgba(186, 26, 26, 1);
--error40_64: rgba(186, 26, 26, 0.64);
--error40_32: rgba(186, 26, 26, 0.32);
--error40_16: rgba(186, 26, 26, 0.16);
--error50: rgba(222, 55, 48, 1);
--error60: rgba(255, 84, 73, 1);
--error70: rgba(255, 137, 125, 1);
--error80: rgba(255, 180, 171, 1);
--error80_64: rgba(255, 180, 171, 0.64);
--error80_32: rgba(255, 180, 171, 0.32);
--error80_16: rgba(255, 180, 171, 0.16);
--error90: rgba(255, 218, 214, 1);
--error95: rgba(255, 237, 234, 1);
}

View File

@@ -1,7 +1,7 @@
:root {
--corner-radius_s: 8px;
--corner-radius_m: 16px;
--corner-radius_l: 24px;
--corner-radius_xl: 32px;
--corner-radius_circular: 9999px;
}
:root {
--corner-radius_s: 8px;
--corner-radius_m: 16px;
--corner-radius_l: 24px;
--corner-radius_xl: 32px;
--corner-radius_circular: 9999px;
}

View File

@@ -1,108 +1,106 @@
@import "src/tokens/index";
:root {
--uu-font-family: "Figtree", "Arial", "Roboto", sans-serif;
--uu-font-family-code: "Roboto Mono", monospace;
--weight_regular: 400;
--weight_semibold: 600;
--weight_bold: 700;
--weight_extrabold: 800;
--weight_black: 900;
--uu-font-family: "Figtree", "Arial", "Roboto", sans-serif;
--uu-font-family-code: "Roboto Mono", monospace;
--weight_regular: 400;
--weight_semibold: 600;
--weight_bold: 700;
--weight_extrabold: 800;
--weight_black: 900;
--h1_font-size: #{pxToRem(32)};
--h1_line-height: 100%;
--h1_block-padding-top: #{pxToRem(16)};
--h1_block-padding-bottom: #{pxToRem(16)};
--h1_font-size: #{pxToRem(32)};
--h1_line-height: 100%;
--h1_block-padding-top: #{pxToRem(16)};
--h1_block-padding-bottom: #{pxToRem(16)};
--h2_font-size: #{pxToRem(28)};
--h2_line-height: #{pxToRem(32)};
--h2_block-padding-top: #{pxToRem(16)};
--h2_block-padding-bottom: #{pxToRem(8)};
--h2_font-size: #{pxToRem(28)};
--h2_line-height: #{pxToRem(32)};
--h2_block-padding-top: #{pxToRem(16)};
--h2_block-padding-bottom: #{pxToRem(8)};
--h3_font-size: #{pxToRem(24)};
--h3_line-height: #{pxToRem(28)};
--h3_block-padding-top: #{pxToRem(16)};
--h3_block-padding-bottom: #{pxToRem(4)};
--h3_font-size: #{pxToRem(24)};
--h3_line-height: #{pxToRem(28)};
--h3_block-padding-top: #{pxToRem(16)};
--h3_block-padding-bottom: #{pxToRem(4)};
--h4_font-size: #{pxToRem(22)};
--h4_line-height: #{pxToRem(24)};
--h4_block-padding-top: #{pxToRem(16)};
--h4_block-padding-bottom: #{pxToRem(0)};
--h4_font-size: #{pxToRem(22)};
--h4_line-height: #{pxToRem(24)};
--h4_block-padding-top: #{pxToRem(16)};
--h4_block-padding-bottom: #{pxToRem(0)};
--h5_font-size: #{pxToRem(20)};
--h5_line-height: #{pxToRem(24)};
--h5_block-padding-top: #{pxToRem(16)};
--h5_block-padding-bottom: #{pxToRem(0)};
--h5_font-size: #{pxToRem(20)};
--h5_line-height: #{pxToRem(24)};
--h5_block-padding-top: #{pxToRem(16)};
--h5_block-padding-bottom: #{pxToRem(0)};
--h6_font-size: #{pxToRem(18)};
--h6_line-height: #{pxToRem(20)};
--h6_block-padding-top: #{pxToRem(16)};
--h6_block-padding-bottom: #{pxToRem(0)};
--h6_font-size: #{pxToRem(18)};
--h6_line-height: #{pxToRem(20)};
--h6_block-padding-top: #{pxToRem(16)};
--h6_block-padding-bottom: #{pxToRem(0)};
--p_block-padding-vertical: #{pxToRem(16)};
--p_block-padding-vertical: #{pxToRem(16)};
--p_large_font-size: #{pxToRem(16)};
--p_large_line-height: #{pxToRem(20)};
--p_large_font-size: #{pxToRem(16)};
--p_large_line-height: #{pxToRem(20)};
--p_medium_font-size: #{pxToRem(14)};
--p_medium_line-height: #{pxToRem(20)};
--p_medium_font-size: #{pxToRem(14)};
--p_medium_line-height: #{pxToRem(20)};
--p_small_font-size: #{pxToRem(12)};
--p_small_line-height: #{pxToRem(16)};
--p_small_font-size: #{pxToRem(12)};
--p_small_line-height: #{pxToRem(16)};
--button_block-padding-vertical: var(--spc-3x);
--button_block-padding-vertical: var(--spc-3x);
--button_large_font-size: #{pxToRem(16)};
--button_large_line-height: #{pxToRem(20)};
--button_large_font-size: #{pxToRem(16)};
--button_large_line-height: #{pxToRem(20)};
--button_regular_font-size: #{pxToRem(14)};
--button_regular_line-height: #{pxToRem(20)};
--button_regular_font-size: #{pxToRem(14)};
--button_regular_line-height: #{pxToRem(20)};
@include from($desktopSmall) {
--h1_font-size: #{pxToRem(48)};
--h1_line-height: 100%;
--h1_block-padding-top: #{pxToRem(24)};
--h1_block-padding-bottom: #{pxToRem(24)};
@include from($desktopSmall) {
--h2_font-size: #{pxToRem(40)};
--h2_line-height: 100%;
--h2_block-padding-top: #{pxToRem(24)};
--h2_block-padding-bottom: #{pxToRem(16)};
--h1_font-size: #{pxToRem(48)};
--h1_line-height: 100%;
--h1_block-padding-top: #{pxToRem(24)};
--h1_block-padding-bottom: #{pxToRem(24)};
--h3_font-size: #{pxToRem(32)};
--h3_line-height: #{pxToRem(36)};
--h3_block-padding-top: #{pxToRem(24)};
--h3_block-padding-bottom: #{pxToRem(8)};
--h2_font-size: #{pxToRem(40)};
--h2_line-height: 100%;
--h2_block-padding-top: #{pxToRem(24)};
--h2_block-padding-bottom: #{pxToRem(16)};
--h4_font-size: #{pxToRem(28)};
--h4_line-height: #{pxToRem(32)};
--h4_block-padding-top: #{pxToRem(24)};
--h4_block-padding-bottom: #{pxToRem(8)};
--h3_font-size: #{pxToRem(32)};
--h3_line-height: #{pxToRem(36)};
--h3_block-padding-top: #{pxToRem(24)};
--h3_block-padding-bottom: #{pxToRem(8)};
--h5_font-size: #{pxToRem(24)};
--h5_line-height: #{pxToRem(28)};
--h5_block-padding-top: #{pxToRem(24)};
--h5_block-padding-bottom: #{pxToRem(8)};
--h4_font-size: #{pxToRem(28)};
--h4_line-height: #{pxToRem(32)};
--h4_block-padding-top: #{pxToRem(24)};
--h4_block-padding-bottom: #{pxToRem(8)};
--h6_font-size: #{pxToRem(20)};
--h6_line-height: #{pxToRem(24)};
--h6_block-padding-top: #{pxToRem(24)};
--h6_block-padding-bottom: #{pxToRem(8)};
--h5_font-size: #{pxToRem(24)};
--h5_line-height: #{pxToRem(28)};
--h5_block-padding-top: #{pxToRem(24)};
--h5_block-padding-bottom: #{pxToRem(8)};
--p_block-padding-vertical: #{pxToRem(24)};
--h6_font-size: #{pxToRem(20)};
--h6_line-height: #{pxToRem(24)};
--h6_block-padding-top: #{pxToRem(24)};
--h6_block-padding-bottom: #{pxToRem(8)};
--p_large_font-size: #{pxToRem(18)};
--p_large_line-height: #{pxToRem(24)};
--p_block-padding-vertical: #{pxToRem(24)};
--p_medium_font-size: #{pxToRem(16)};
--p_medium_line-height: #{pxToRem(20)};
--p_large_font-size: #{pxToRem(18)};
--p_large_line-height: #{pxToRem(24)};
--p_small_font-size: #{pxToRem(14)};
--p_small_line-height: #{pxToRem(20)};
--p_medium_font-size: #{pxToRem(16)};
--p_medium_line-height: #{pxToRem(20)};
--p_small_font-size: #{pxToRem(14)};
--p_small_line-height: #{pxToRem(20)};
--button_large_font-size: #{pxToRem(18)};
--button_large_line-height: #{pxToRem(24)};
}
--button_large_font-size: #{pxToRem(18)};
--button_large_line-height: #{pxToRem(24)};
}
}

View File

@@ -1,15 +1,15 @@
:root {
/*
** Default size for icons.
*/
--icon-size_regular: 1.5rem;
/*
** Size for dense layouts with small text sizes.
** The fixed dimension and the padding serve to
** automatically resize the icon to the desired 18pt.
*/
--icon-size_dense: 1.25rem;
--icon-size-dense-padding: 0.0625rem;
};
:root {
/*
** Default size for icons.
*/
--icon-size_regular: 1.5rem;
/*
** Size for dense layouts with small text sizes.
** The fixed dimension and the padding serve to
** automatically resize the icon to the desired 18pt.
*/
--icon-size_dense: 1.25rem;
--icon-size-dense-padding: 0.0625rem;
}

View File

@@ -1,7 +1,7 @@
:root {
--max-width_xs: 480px;
--max-width_s: 640px;
--max-width_m: 960px;
--max-width_l: 1280px;
--max-width_xl: 1920px;
};
:root {
--max-width_xs: 480px;
--max-width_s: 640px;
--max-width_m: 960px;
--max-width_l: 1280px;
--max-width_xl: 1920px;
}

View File

@@ -1,6 +1,10 @@
:root {
--shadow_sticker_light: 0px 1px 1.5px rgba(0, 0, 0, 0.24), 0px 0.5px 0px rgba(0, 0, 0, 0.12);
--shadow_popup_light: 0px 8px 16px rgba(79, 97, 109, 0.24), 0px 4px 6px rgba(79, 97, 109, 0.24);
--shadow_sticker_dark: 0px 1px 1.5px rgba(0, 0, 0, 0.72), 0px 0.5px 0px rgba(0, 0, 0, 0.36);
--shadow_popup_dark: 0px 8px 16px rgba(0, 0, 0, 0.72), 0px 4px 6px rgba(0, 0, 0, 0.72);
}
:root {
--shadow_sticker_light: 0px 1px 1.5px rgba(0, 0, 0, 0.24),
0px 0.5px 0px rgba(0, 0, 0, 0.12);
--shadow_popup_light: 0px 8px 16px rgba(79, 97, 109, 0.24),
0px 4px 6px rgba(79, 97, 109, 0.24);
--shadow_sticker_dark: 0px 1px 1.5px rgba(0, 0, 0, 0.72),
0px 0.5px 0px rgba(0, 0, 0, 0.36);
--shadow_popup_dark: 0px 8px 16px rgba(0, 0, 0, 0.72),
0px 4px 6px rgba(0, 0, 0, 0.72);
}

View File

@@ -20,11 +20,11 @@
--chip-gap: var(--spc-2x);
--site-spacing: var(--spc-4x);
@include from($tabletLarge) {
--site-spacing: var(--spc-6x);
}
@include from($tabletLarge) {
--site-spacing: var(--spc-6x);
}
@include from($desktopSmall) {
--site-spacing: var(--spc-8x);
}
@include from($desktopSmall) {
--site-spacing: var(--spc-8x);
}
}

View File

@@ -1,6 +1,6 @@
:root {
--min-target-size_s: 32px;
--min-target-size_m: 40px;
--min-target-size_l: 48px;
--min-target-size_xl: 56px;
}
:root {
--min-target-size_s: 32px;
--min-target-size_m: 40px;
--min-target-size_l: 48px;
--min-target-size_xl: 56px;
}

View File

@@ -1,17 +1,17 @@
$desktopLarge: 1920px;
$desktopSmall: 1280px;
$tabletLarge: 960px;
$tabletSmall: 640px;
$mobile: 400px;
@mixin until($breakpoint) {
@media screen and (max-width: $breakpoint) {
@content;
}
}
@mixin from($breakpoint) {
@media screen and (min-width: $breakpoint) {
@content;
}
}
$desktopLarge: 1920px;
$desktopSmall: 1280px;
$tabletLarge: 960px;
$tabletSmall: 640px;
$mobile: 400px;
@mixin until($breakpoint) {
@media screen and (max-width: $breakpoint) {
@content;
}
}
@mixin from($breakpoint) {
@media screen and (min-width: $breakpoint) {
@content;
}
}

View File

@@ -1,3 +1,3 @@
@import "./_breakpoints.scss";
@import "./_utils.scss";
@import "./_vars.scss";
@import "./_breakpoints.scss";
@import "./_utils.scss";
@import "./_vars.scss";

View File

@@ -3,6 +3,6 @@ export {};
declare global {
const plausible: (
val: string,
args?: { props?: any; callback?: () => void }
args?: { props?: unknown; callback?: () => void },
) => void;
}

View File

@@ -8,16 +8,16 @@ export function getPostsByLang(language: Languages): PostInfo[] {
export function getPostsByUnicorn(
authorId: string,
language: Languages
language: Languages,
): PostInfo[] {
return getPostsByLang(language).filter((post) =>
post.authors.find((postAuthor) => postAuthor === authorId)
post.authors.find((postAuthor) => postAuthor === authorId),
);
}
export function getPostsByCollection(
collection: string,
language: Languages
language: Languages,
): PostInfo[] {
return getPostsByLang(language)
.filter((post) => post.collection === collection)

View File

@@ -48,12 +48,12 @@ const fullUnicorns: UnicornInfo[] = unicornsRaw.map((unicorn) => {
*/
const relativeServerPath = getFullRelativePath(
"/content/data/",
unicorn.profileImg
unicorn.profileImg,
);
const profileImgSize = getImageSize(
unicorn.profileImg,
dataDirectory,
dataDirectory
dataDirectory,
);
// Mutation go BRR
@@ -68,7 +68,7 @@ const fullUnicorns: UnicornInfo[] = unicornsRaw.map((unicorn) => {
};
newUnicorn.rolesMeta = unicorn.roles.map(
(role) => rolesRaw.find((rRole) => rRole.id === role)! as RolesEnum
(role) => rolesRaw.find((rRole) => rRole.id === role)! as RolesEnum,
);
// normalize social links - if a URL or "@name" is entered, only preserve the last part
@@ -99,7 +99,7 @@ function getCollections(): Array<CollectionInfo> {
return files.map((file, i): CollectionInfo => {
const fileContents = fs.readFileSync(
join(collectionsDirectory, slug, file),
"utf8"
"utf8",
);
const frontmatter = matter(fileContents).data as RawCollectionInfo;
@@ -107,7 +107,7 @@ function getCollections(): Array<CollectionInfo> {
const coverImgSize = getImageSize(
frontmatter.coverImg,
join(collectionsDirectory, slug),
join(collectionsDirectory, slug)
join(collectionsDirectory, slug),
);
const coverImgMeta = {
@@ -116,13 +116,13 @@ function getCollections(): Array<CollectionInfo> {
relativePath: frontmatter.coverImg,
relativeServerPath: getFullRelativePath(
`/content/collections/${slug}`,
frontmatter.coverImg
frontmatter.coverImg,
),
absoluteFSPath: join(collectionsDirectory, slug, frontmatter.coverImg),
};
const authorsMeta = frontmatter.authors.map((authorId) =>
fullUnicorns.find((u) => u.id === authorId)
fullUnicorns.find((u) => u.id === authorId),
);
return {
@@ -156,7 +156,7 @@ function getPosts(): Array<PostInfo> {
return files.map((file, i): PostInfo => {
const fileContents = fs.readFileSync(
join(postsDirectory, slug, file),
"utf8"
"utf8",
);
const frontmatter = matter(fileContents).data as RawPostInfo;
@@ -176,7 +176,7 @@ function getPosts(): Array<PostInfo> {
locales,
locale: locales[i],
authorsMeta: frontmatter.authors.map((authorId) =>
fullUnicorns.find((u) => u.id === authorId)
fullUnicorns.find((u) => u.id === authorId),
),
wordCount: (counts.InlineCodeWords || 0) + (counts.WordNode || 0),
publishedMeta:

View File

@@ -5,10 +5,10 @@
export function debounce<T extends Function>(
func: T,
wait: number,
immediate: boolean
immediate: boolean,
): T {
let timeout: number | null;
return function (this: any) {
return function (this: unknown) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;
// eslint-disable-next-line prefer-rest-params

View File

@@ -57,13 +57,13 @@ const getOrderRange = (arr: PostInfo[]) => {
{
largest: null as PostInfo,
smallest: null as PostInfo,
}
},
);
};
export const getSuggestedArticles = (
postNode: ExtendedPostInfo,
lang: Languages
lang: Languages,
) => {
const suggestedPosts = getPostsByLang(lang);
@@ -119,7 +119,7 @@ export const getSuggestedArticles = (
}
const howManyTagsSimilar = howManySimilarBetween(
post.tags,
postNode.tags || []
postNode.tags || [],
);
if (howManyTagsSimilar > 0) {
similarTags.push({ post, howManyTagsSimilar });

View File

@@ -16,7 +16,7 @@ const unicornProfilePicMap = Promise.all(
alt: "",
})),
id: unicorn.id,
}))
})),
);
export const getUnicornProfilePicMap = async () => {

View File

@@ -26,7 +26,7 @@ const FolderIcon = makeSVGIcon(
`<svg viewBox="0 0 20 20">
<path d="M4 3C2.89543 3 2 3.89543 2 5V15C2 16.1046 2.89543 17 4 17H16C17.1046 17 18 16.1046 18 15V8C18 6.89543 17.1046 6 16 6H11L10.0528 4.10557C9.714 3.428 9.02148 3 8.26393 3H4Z"/>
</svg>
`
`,
);
export interface File {

View File

@@ -6,7 +6,7 @@ import {
import squoosh_service from "../../../node_modules/@astrojs/image/dist/loaders/squoosh.js";
export function getPicture(
params: GetPictureParams
params: GetPictureParams,
): Promise<GetPictureResult> {
// HACK: This is a hack that heavily relies on `getImage`'s internals :(
globalThis.astroImage = {

View File

@@ -8,7 +8,7 @@ const chevron_down = await fs.readFile("src/icons/chevron_down.svg", "utf8");
interface HintProps {
title: string;
children: Node[];
};
}
/** @jsxImportSource hastscript */
export function Hint({ title, children }: HintProps): Element {
@@ -20,9 +20,7 @@ export function Hint({ title, children }: HintProps): Element {
{title}
</summary>
<div class="hint__content">
{children}
</div>
<div class="hint__content">{children}</div>
</details>
</div>
) as never;

View File

@@ -16,7 +16,11 @@ export interface IFramePlaceholderProps {
}
/** @jsxImportSource hastscript */
export function IFramePlaceholder({ height, width, ...props }: IFramePlaceholderProps): Element {
export function IFramePlaceholder({
height,
width,
...props
}: IFramePlaceholderProps): Element {
return (
<div class="embed">
<div class="embed__header">
@@ -26,7 +30,7 @@ export function IFramePlaceholder({ height, width, ...props }: IFramePlaceholder
<source {...source} />
))}
<img
{...props.pageIcon.image as any}
{...(props.pageIcon.image as any)}
alt=""
loading="lazy"
decoding="async"
@@ -54,9 +58,7 @@ export function IFramePlaceholder({ height, width, ...props }: IFramePlaceholder
target="_blank"
>
<div class="buttonIcon">{fromHtml(launch)}</div>
<div class="innerText">
New tab
</div>
<div class="innerText">New tab</div>
</a>
</div>
<div

View File

@@ -3,7 +3,7 @@
*/
export const iFrameClickToRun = () => {
const iframeButtons: HTMLElement[] = document.querySelectorAll(
"[data-iframeurl] > button"
"[data-iframeurl] > button",
) as never;
[...iframeButtons].forEach((el) => {

View File

@@ -40,7 +40,7 @@ function fetchPageIcon(src: URL, srcHast: Root): Promise<GetPictureResult> {
// <link rel="manifest" href="/manifest.json">
const manifestPath: Element = find(
srcHast,
(node: unknown) => (node as Element)?.properties?.rel?.[0] === "manifest"
(node: unknown) => (node as Element)?.properties?.rel?.[0] === "manifest",
);
let iconLink: string;
@@ -63,8 +63,10 @@ function fetchPageIcon(src: URL, srcHast: Root): Promise<GetPictureResult> {
if (!iconLink) {
// fetch `favicon.ico`
// <link rel="shortcut icon" type="image/png" href="https://example.com/img.png">
const favicon: Element = find(srcHast, (node: unknown) =>
(node as Element)?.properties?.rel?.toString()?.includes("icon")
const favicon: Element = find(
srcHast,
(node: unknown) =>
(node as Element)?.properties?.rel?.toString()?.includes("icon"),
);
if (favicon) {
@@ -155,7 +157,7 @@ export const rehypeUnicornIFrameClickToRun: Plugin<
const width = iframeNode.properties.width ?? EMBED_SIZE.w;
const height = iframeNode.properties.height ?? EMBED_SIZE.h;
const info: PageInfo = (await fetchPageInfo(
iframeNode.properties.src.toString()
iframeNode.properties.src.toString(),
).catch(() => null)) || { icon: await fetchDefaultPageIcon() };
const iframeReplacement = IFramePlaceholder({
@@ -167,7 +169,7 @@ export const rehypeUnicornIFrameClickToRun: Plugin<
});
Object.assign(iframeNode, iframeReplacement);
})
}),
);
};
};

View File

@@ -8,29 +8,29 @@ import { visit } from "unist-util-visit";
* Plugin to add `data-header-text`s to headings.
*/
export const rehypeHeaderText = () => {
return (tree: Root, file) => {
visit(tree, "element", (node: Parent["children"][number]) => {
if (
headingRank(node) &&
"properties" in node &&
node.properties &&
!hasProperty(node, "data-header-text")
) {
const headerText = toString(node);
node.properties["data-header-text"] = headerText;
return (tree: Root, file) => {
visit(tree, "element", (node: Parent["children"][number]) => {
if (
headingRank(node) &&
"properties" in node &&
node.properties &&
!hasProperty(node, "data-header-text")
) {
const headerText = toString(node);
node.properties["data-header-text"] = headerText;
const headingWithID = {
value: headerText,
depth: headingRank(node)!,
slug: node.properties["id"] as string,
};
const headingWithID = {
value: headerText,
depth: headingRank(node)!,
slug: node.properties["id"] as string,
};
if (file.data.astro.frontmatter.headingsWithId) {
file.data.astro.frontmatter.headingsWithId.push(headingWithID);
} else {
file.data.astro.frontmatter.headingsWithId = [headingWithID];
}
}
});
};
if (file.data.astro.frontmatter.headingsWithId) {
file.data.astro.frontmatter.headingsWithId.push(headingWithID);
} else {
file.data.astro.frontmatter.headingsWithId = [headingWithID];
}
}
});
};
};

View File

@@ -25,7 +25,7 @@ export const rehypeUnicornElementMap: Plugin<[], Root> = () => {
"/content/",
parentFolder,
slug,
node.properties.src.toString()
node.properties.src.toString(),
);
}

View File

@@ -4,7 +4,7 @@ export const enableTables = () => {
(e.target as HTMLElement).dataset.sticky =
e.intersectionRatio < 1 ? "pinned" : "";
},
{ threshold: [1] }
{ threshold: [1] },
);
document.querySelectorAll("thead").forEach((e) => observer.observe(e));

View File

@@ -33,7 +33,7 @@ const getApproxLineCount = (nodes: Node[], inParagraph?: boolean): number => {
if ("children" in n) {
lines += getApproxLineCount(
(n as Parent).children as Node[],
isInParagraph
isInParagraph,
);
}
// assume that any div/p/br causes a line break
@@ -125,7 +125,7 @@ export const rehypeTabs: Plugin<[], Root> = () => {
// Determine if the set of tabs should use a constant height (via the "tabs-small" class)
const tabHeights = tabs.map(({ contents }) =>
getApproxLineCount(contents)
getApproxLineCount(contents),
);
const isSmall =
// all tabs must be <= 30 approx. lines (less than the height of most desktop viewports)
@@ -145,13 +145,13 @@ export const rehypeTabs: Plugin<[], Root> = () => {
tree,
{ type: "raw", value: "<!-- tabs:start -->" } as never,
{ type: "raw", value: "<!-- tabs:end -->" } as never,
replaceTabNodes
replaceTabNodes,
);
replaceAllBetween(
tree,
{ type: "comment", value: " tabs:start " } as never,
{ type: "comment", value: " tabs:end " } as never,
replaceTabNodes
replaceTabNodes,
);
return tree;
};

View File

@@ -85,7 +85,7 @@ export const enableTabs = () => {
tabs.forEach((tab) => {
const panel = parent.querySelector<HTMLElement>(
`#${tab.getAttribute("aria-controls")}`
`#${tab.getAttribute("aria-controls")}`,
);
entry.set(tab.dataset.tabname, {
tab,

View File

@@ -14,7 +14,7 @@ export interface TabInfo {
interface TabsProps {
tabs: TabInfo[];
isSmall: boolean;
};
}
/** @jsxImportSource hastscript */
export function Tabs({ tabs, isSmall }: TabsProps): Element {
@@ -46,9 +46,7 @@ export function Tabs({ tabs, isSmall }: TabsProps): Element {
aria-labelledby={`tab-${index}`}
aria-hidden={index === 0 ? undefined : true}
>
<div class="tabs__tab-panel__inner">
{contents}
</div>
<div class="tabs__tab-panel__inner">{contents}</div>
</div>
))}
</div>

View File

@@ -10,7 +10,7 @@ interface TooltipProps {
icon: "info" | "warning";
title: string;
children: Node[];
};
}
/** @jsxImportSource hastscript */
export function Tooltip({ icon, title, children }: TooltipProps): Element {
@@ -20,9 +20,7 @@ export function Tooltip({ icon, title, children }: TooltipProps): Element {
{icon === "info" ? fromHtml(info) : fromHtml(warning)}
<p>{title}</p>
</div>
<div class="tooltip__content">
{children}
</div>
<div class="tooltip__content">{children}</div>
</blockquote>
) as never;
}

View File

@@ -32,7 +32,7 @@ describe("utils/translations.ts", () => {
test("returns 'fr' from '/posts/test/index.fr.md'", () => {
const lang = translations.getLanguageFromFilename(
"/posts/test/index.fr.md"
"/posts/test/index.fr.md",
);
expect(lang).toBe("fr");
});
@@ -42,7 +42,7 @@ describe("utils/translations.ts", () => {
test("returns an initial prefix", () => {
const expected: Languages = "fr";
const actual = translations.getPrefixLanguageFromPath(
`/${expected}/something/extra/en/fr/hi`
`/${expected}/something/extra/en/fr/hi`,
);
expect(actual).toEqual(expected);
@@ -51,7 +51,7 @@ describe("utils/translations.ts", () => {
test("returns an initial prefix with no preceding slash", () => {
const expected: Languages = "fr";
const actual = translations.getPrefixLanguageFromPath(
`${expected}/something/extra/en/fr/hi`
`${expected}/something/extra/en/fr/hi`,
);
expect(actual).toEqual(expected);
@@ -69,7 +69,7 @@ describe("utils/translations.ts", () => {
test("removes an initial prefix", () => {
const lang: Languages = "fr";
const actual = translations.removePrefixLanguageFromPath(
`/${lang}/something/extra/hi`
`/${lang}/something/extra/hi`,
);
expect(actual).toEqual("/something/extra/hi");
@@ -78,7 +78,7 @@ describe("utils/translations.ts", () => {
test("removes an initial prefix with no preceding slash", () => {
const lang: Languages = "fr";
const actual = translations.removePrefixLanguageFromPath(
`${lang}/something/extra/hi`
`${lang}/something/extra/hi`,
);
expect(actual).toEqual("something/extra/hi");
@@ -94,7 +94,7 @@ describe("utils/translations.ts", () => {
test("is not confused by prefixes that appear after the start of the path", () => {
const lang: Languages = "en";
const actual = translations.removePrefixLanguageFromPath(
`/${lang}/${lang}/es/fr/something/hi`
`/${lang}/${lang}/es/fr/something/hi`,
);
expect(actual).toEqual(`/${lang}/es/fr/something/hi`);

View File

@@ -14,7 +14,7 @@ function isLanguageKey(str: string): str is Languages {
* code handles the parsing and converting of translation formats
*/
export function fileToOpenGraphConverter<T extends Languages>(
lang: T
lang: T,
): T extends `${infer Lang}-${infer Region}`
? `${Lang}_${Uppercase<Region>}`
: T {
@@ -89,7 +89,7 @@ export function removePrefixLanguageFromPath(path: string) {
*/
export function getTranslatedPage(
astro: { url: URL },
glob: MarkdownInstance<Record<string, unknown>>[]
glob: MarkdownInstance<Record<string, unknown>>[],
): {
locales: Languages[];
page: MarkdownInstance<Record<string, unknown>>;
@@ -98,7 +98,7 @@ export function getTranslatedPage(
const lang = getPrefixLanguageFromPath(astro.url.pathname);
const matchedResult = globResults.find((md) =>
md.file.endsWith(`${lang}.md`)
md.file.endsWith(`${lang}.md`),
);
const enResult = globResults.find((md) => md.file.split(".")[1] === "md");
@@ -128,8 +128,8 @@ const i18n: Partial<Record<Languages, Map<string, string>>> =
([file, content]: [string, { default: Record<string, string> }]) => [
basename(file).split(".")[0],
new Map(Object.entries(content.default)),
]
)
],
),
);
// warn about any values that do not have full translations
@@ -140,7 +140,7 @@ for (const key of i18n.en?.keys() || []) {
if (missing.length) {
console.log(
`i18n: key "${key}" is missing from /content/data/i18n for languages: ${missing}`
`i18n: key "${key}" is missing from /content/data/i18n for languages: ${missing}`,
);
}
}
@@ -157,7 +157,7 @@ export function translate(astro: { url: URL }, key: string, ...args: string[]) {
if (!value) {
console.warn(
`Translation key "${key}" is not specified in /content/data/i18n/${lang}.json`
`Translation key "${key}" is not specified in /content/data/i18n/${lang}.json`,
);
value = i18n.en?.get(key);
}

View File

@@ -28,7 +28,7 @@
margin: var(--spc-2x);
background-color: #FFF;
background-color: #fff;
border-radius: var(--corner-radius_s);
overflow: hidden;
}

View File

@@ -1,8 +1,8 @@
.translationContainer {
font-weight: bold;
border: var(--cardOutlineStyle);
border-radius: var(--cardRadius);
padding: 0.75rem;
background: var(--cardActiveBackground);
margin: 1rem 0;
font-weight: bold;
border: var(--cardOutlineStyle);
border-radius: var(--cardRadius);
padding: 0.75rem;
background: var(--cardActiveBackground);
margin: 1rem 0;
}

View File

@@ -2,7 +2,7 @@
.text {
margin: 0 !important;
margin-left: var(--spc-2x) !important;;
margin-left: var(--spc-2x) !important;
}
.card {

View File

@@ -15,8 +15,12 @@
--article-nav_item_overline-color: var(--primary_default);
--article-nav_item_background-color: var(--transparent);
--article-nav_item_background-color_hovered: var(--surface_primary_emphasis-low);
--article-nav_item_background-color_pressed: var(--surface_primary_emphasis-low);
--article-nav_item_background-color_hovered: var(
--surface_primary_emphasis-low
);
--article-nav_item_background-color_pressed: var(
--surface_primary_emphasis-low
);
--article-nav_item_background-color_focused: var(--background_focus);
--article-nav_item_border_color: var(--surface_primary_emphasis-low);
@@ -42,10 +46,12 @@
display: flex;
flex-direction: column;
gap: var(--article-nav_item_gap);
padding: var(--article-nav_item_padding-vertical) var(--article-nav_item_padding-horizontal);
padding: var(--article-nav_item_padding-vertical)
var(--article-nav_item_padding-horizontal);
background-color: var(--article-nav_item_background-color);
border: var(--article-nav_item_border-width) solid var(--article-nav_item_border_color);
border: var(--article-nav_item_border-width) solid
var(--article-nav_item_border_color);
border-radius: var(--article-nav_item_corner-radius);
@include transition(background-color border-color);

View File

@@ -16,30 +16,28 @@ function ArticleNavItem({ post, type }: ArticleNavItemProps) {
class={`${style.item} ${style[`item--${type}`]}`}
data-navigation-path={href}
>
{
type === "previous"
? (
<span class={`${style.item__overline} text-style-button-regular`}>
<span
class={`${style.icon}`}
dangerouslySetInnerHTML={{ __html: arrow_left }}
/>
Previous article
</span>
)
: (
<span class={`${style.item__overline} text-style-button-regular`}>
Next article
<span
class={`${style.icon}`}
dangerouslySetInnerHTML={{ __html: arrow_right }}
/>
</span>
)
}
<a href={href} class="text-style-body-medium-bold">{getShortTitle(post)}</a>
{type === "previous" ? (
<span class={`${style.item__overline} text-style-button-regular`}>
<span
class={`${style.icon}`}
dangerouslySetInnerHTML={{ __html: arrow_left }}
/>
Previous article
</span>
) : (
<span class={`${style.item__overline} text-style-button-regular`}>
Next article
<span
class={`${style.icon}`}
dangerouslySetInnerHTML={{ __html: arrow_right }}
/>
</span>
)}
<a href={href} class="text-style-body-medium-bold">
{getShortTitle(post)}
</a>
</div>
)
);
}
export interface ArticleNavProps {
@@ -57,5 +55,5 @@ export function ArticleNav({ post, postSeries }: ArticleNavProps) {
{prevPost && <ArticleNavItem post={prevPost} type="previous" />}
{nextPost && <ArticleNavItem post={nextPost} type="next" />}
</div>
)
);
}

View File

@@ -14,7 +14,8 @@
padding: var(--site-spacing);
}
.sidebarLeft, .sidebarRight {
.sidebarLeft,
.sidebarRight {
flex-basis: 25%;
flex-grow: 0;
flex-shrink: 0;
@@ -42,8 +43,6 @@
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;

View File

@@ -33,7 +33,8 @@
padding: var(--article-header_details_padding-vertical) 0;
.date, .authors {
.date,
.authors {
display: flex;
align-items: center;
@@ -59,7 +60,7 @@
color: var(--article-header_details_date_edited_color);
&::before {
content: '';
content: "";
margin-left: var(--article-header_details_date-gap);
padding-right: var(--article-header_details_date-gap);
}

View File

@@ -16,7 +16,7 @@ interface ActivePostMeta extends ExtendedPostInfo {
export function findActivePost(
post: ExtendedPostInfo,
seriesPosts: ExtendedPostInfo[]
seriesPosts: ExtendedPostInfo[],
) {
const newPosts = [...seriesPosts] as ActivePostMeta[];

View File

@@ -147,7 +147,6 @@
height: 100%;
width: var(--series-nav_chapter-item_border_width);
background: var(--series-nav_chapter-item_border_color_selected);
}
.navigationItem[data-is-active]::after {

View File

@@ -1,7 +1,6 @@
@import "src/tokens/index";
:root {
--toc_header_padding-top: var(--site-spacing);
--toc_header_padding-horizontal: var(--site-spacing);
--toc_header_padding-bottom: var(--spc-4x);
@@ -63,7 +62,8 @@
}
.tocTitle {
margin: var(--toc_header_padding-top) var(--toc_header_padding-horizontal) var(--toc_header_padding-bottom);
margin: var(--toc_header_padding-top) var(--toc_header_padding-horizontal)
var(--toc_header_padding-bottom);
}
.tableList {
@@ -89,7 +89,8 @@
}
.tableListItem:not(.depth2) > a {
padding: var(--toc_item_padding-vertical) var(--toc_item_padding-end) var(--toc_item_padding-vertical) var(--toc_item_padding-start);
padding: var(--toc_item_padding-vertical) var(--toc_item_padding-end)
var(--toc_item_padding-vertical) var(--toc_item_padding-start);
}
.tableListItem:hover > a {
@@ -105,7 +106,8 @@
pointer-events: none;
}
.tableListItem:hover > a > *, :global(.toc-is-active) .tableListItemLinkInner {
.tableListItem:hover > a > *,
:global(.toc-is-active) .tableListItemLinkInner {
color: var(--foreground_emphasis-high);
}
@@ -113,7 +115,10 @@
}
.depth2 > a > * {
margin-left: calc(var(--toc_sub-item_dot_size) + var(--toc_sub-item_dot_margin-end) + var(--toc_sub-item_padding-start));
margin-left: calc(
var(--toc_sub-item_dot_size) + var(--toc_sub-item_dot_margin-end) +
var(--toc_sub-item_padding-start)
);
position: relative;
}
@@ -122,41 +127,64 @@
border: none !important;
}
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active) > a:not(:focus-visible) > *::before {
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active)
> a:not(:focus-visible)
> *::before {
content: " ";
background: var(--toc_divider_color);
height: 100%;
width: 2px;
position: absolute;
left: calc(-2px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size) / 2));
left: calc(
-2px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size) /
2)
);
top: 0;
}
/* Extended line */
@supports selector(:has(*)) {
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(:has(:focus-visible))
+ .depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(:has(:focus-visible))
> a > *::before {
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(
:has(:focus-visible)
)
+ .depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(
:has(:focus-visible)
)
> a
> *::before {
content: " ";
background: var(--toc_divider_color);
height: calc(100% + var(--toc_item_min-height));
width: 2px;
position: absolute;
left: calc(-2px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size) / 2));
left: calc(
-2px - var(--toc_sub-item_padding-start) - calc(var(
--toc_sub-item_dot_size
) / 2)
);
bottom: 0;
top: unset;
}
}
@supports not selector(:has(*)) {
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(:focus-within)
+ .depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(:focus-within)
> a > *::before {
.depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(
:focus-within
)
+ .depth2:not(:global(.toc-is-active)):not(:hover):not(:active):not(
:focus-within
)
> a
> *::before {
content: " ";
background: var(--toc_divider_color);
height: calc(100% + var(--toc_item_min-height));
width: 2px;
position: absolute;
left: calc(-2px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size) / 2));
left: calc(
-2px - var(--toc_sub-item_padding-start) - calc(var(
--toc_sub-item_dot_size
) / 2)
);
bottom: 0;
top: unset;
}
@@ -174,7 +202,9 @@
height: 100%;
width: var(--toc_sub-item_dot_size);
position: absolute;
left: calc(0px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size)));
left: calc(
0px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size))
);
top: 0;
}
@@ -192,7 +222,9 @@
height: 100%;
width: var(--toc_sub-item_dot_size);
position: absolute;
left: calc(0px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size)));
left: calc(
0px - var(--toc_sub-item_padding-start) - calc(var(--toc_sub-item_dot_size))
);
top: 0;
}

View File

@@ -1,277 +1,278 @@
@import "src/tokens/index";
:root {
--collection-page_banner_corner-radius: var(--corner-radius_l);
--collection-page_banner_corner-radius: var(--corner-radius_l);
--collection-page_padding: var(--site-spacing);
--collection-page_gap: var(--site-spacing);
--collection-page_padding: var(--site-spacing);
--collection-page_gap: var(--site-spacing);
--collection-page_content_gap: var(--site-spacing);
--collection-page_content_button-gap: var(--spc-4x);
--collection-page_content_gap: var(--site-spacing);
--collection-page_content_button-gap: var(--spc-4x);
--collection-page_title_padding-top: var(--site-spacing);
--collection-page_title_padding-bottom: var(--spc-2x);
--collection-page_title_padding-top: var(--site-spacing);
--collection-page_title_padding-bottom: var(--spc-2x);
--collection-page_title_color: var(--foreground_emphasis-high);
--collection-page_subtitle_color: var(--foreground_emphasis-medium);
--collection-page_description_color: var(--foreground_emphasis-high);
--collection-page_title_color: var(--foreground_emphasis-high);
--collection-page_subtitle_color: var(--foreground_emphasis-medium);
--collection-page_description_color: var(--foreground_emphasis-high);
// Author section
--collection-page_author_padding: var(--spc-6x);
--collection-page_author_corner-radius: var(--corner-radius_l);
--collection-page_author_avatar_size: 48px;
--collection-page_author_avatar_border-width: var(--border-width_m);
--collection-page_author_details_gap: var(--spc-4x);
--collection-page_author_details_dot_padding-horizontal: var(--spc-1x);
--collection-page_author_description_padding-top: var(--spc-4x);
// Author section
--collection-page_author_padding: var(--spc-6x);
--collection-page_author_corner-radius: var(--corner-radius_l);
--collection-page_author_avatar_size: 48px;
--collection-page_author_avatar_border-width: var(--border-width_m);
--collection-page_author_details_gap: var(--spc-4x);
--collection-page_author_details_dot_padding-horizontal: var(--spc-1x);
--collection-page_author_description_padding-top: var(--spc-4x);
--collection-page_author_name_color: var(--foreground_emphasis-high);
--collection-page_author_count-articles_color: var(--foreground_emphasis-high);
--collection-page_author_count-words_color: var(--foreground_emphasis-medium);
--collection-page_author_dot_color: var(--foreground_emphasis-medium);
--collection-page_author_description_color: var(--foreground_emphasis-high);
--collection-page_author_name_color: var(--foreground_emphasis-high);
--collection-page_author_count-articles_color: var(
--foreground_emphasis-high
);
--collection-page_author_count-words_color: var(--foreground_emphasis-medium);
--collection-page_author_dot_color: var(--foreground_emphasis-medium);
--collection-page_author_description_color: var(--foreground_emphasis-high);
@include from($tabletSmall) {
--collection-page_banner_max-width: 240px;
--collection-page_title_padding-top: 0px;
}
@include from($tabletSmall) {
--collection-page_banner_max-width: 240px;
--collection-page_title_padding-top: 0px;
}
@include from($tabletLarge) {
--collection-page_banner_max-width: 400px;
}
@include from($tabletLarge) {
--collection-page_banner_max-width: 400px;
}
@include from($desktopSmall) {
--collection-page_banner_corner-radius: var(--corner-radius_xl);
--collection-page_banner_max-width: 480px;
}
@include from($desktopSmall) {
--collection-page_banner_corner-radius: var(--corner-radius_xl);
--collection-page_banner_max-width: 480px;
}
}
.collectionPageContainer {
padding: var(--collection-page_padding);
max-width: var(--max-width_l);
margin: 0 auto;
padding: var(--collection-page_padding);
max-width: var(--max-width_l);
margin: 0 auto;
}
.collectionPageInnerContainer {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(3, min-content);
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(3, min-content);
@include from($tabletSmall) {
grid-template-columns: min-content 1fr;
grid-template-rows: repeat(2, min-content);
grid-column-gap: var(--collection-page_gap);
}
@include from($tabletSmall) {
grid-template-columns: min-content 1fr;
grid-template-rows: repeat(2, min-content);
grid-column-gap: var(--collection-page_gap);
}
@include from($desktopSmall) {
grid-template-columns: min-content 1fr;
grid-template-rows: min-content 1fr;
grid-column-gap: var(--collection-page_gap);
}
@include from($desktopSmall) {
grid-template-columns: min-content 1fr;
grid-template-rows: min-content 1fr;
grid-column-gap: var(--collection-page_gap);
}
}
.titleAndDescContainer {
@include from($tabletSmall) {
grid-column: 2;
grid-row: 1;
display: flex;
justify-content: center;
flex-direction: column;
}
@include from($tabletSmall) {
grid-column: 2;
grid-row: 1;
display: flex;
justify-content: center;
flex-direction: column;
}
@include from($desktopSmall) {
grid-column: 2;
grid-row: 1;
display: unset;
justify-content: unset;
flex-direction: unset;
}
@include from($desktopSmall) {
grid-column: 2;
grid-row: 1;
display: unset;
justify-content: unset;
flex-direction: unset;
}
}
.title {
color: var(--collection-page_title_color);
margin: 0;
margin-bottom: var(--collection-page_title_padding-bottom);
margin-top: var(--collection-page_title_padding-top);
color: var(--collection-page_title_color);
margin: 0;
margin-bottom: var(--collection-page_title_padding-bottom);
margin-top: var(--collection-page_title_padding-top);
}
.description {
color: var(--collection-page_subtitle_color);
margin: 0;
color: var(--collection-page_subtitle_color);
margin: 0;
}
.contentContainer {
display: flex;
flex-direction: column;
gap: var(--collection-page_content_gap);
margin-top: var(--collection-page_content_gap);
display: flex;
flex-direction: column;
gap: var(--collection-page_content_gap);
margin-top: var(--collection-page_content_gap);
@include from($tabletSmall) {
grid-column: 1 / 3;
grid-row: 2;
}
@include from($tabletSmall) {
grid-column: 1 / 3;
grid-row: 2;
}
@include from($desktopSmall) {
grid-column: 2;
grid-row: 2;
}
@include from($desktopSmall) {
grid-column: 2;
grid-row: 2;
}
}
.content>*:first-child {
margin-top: 0 !important;
.content > *:first-child {
margin-top: 0 !important;
}
.content>*:last-child {
margin-bottom: 0 !important;
.content > *:last-child {
margin-bottom: 0 !important;
}
.coverImage {
@include from($tabletSmall) {
grid-column: 1;
grid-row: 1;
}
@include from($tabletSmall) {
grid-column: 1;
grid-row: 1;
}
@include from($desktopSmall) {
grid-column: 1;
grid-row: 1 / 3;
}
@include from($desktopSmall) {
grid-column: 1;
grid-row: 1 / 3;
}
}
.coverImage img {
max-width: 368px;
max-height: 552px;
width: 100%;
max-width: 368px;
max-height: 552px;
width: 100%;
border-radius: var(--collection-page_banner_corner-radius);
border-radius: var(--collection-page_banner_corner-radius);
margin: 0 auto;
margin-bottom: var(--collection-page_gap);
display: block;
margin: 0 auto;
margin-bottom: var(--collection-page_gap);
display: block;
@include from($tabletSmall) {
margin: unset;
display: unset;
max-width: unset;
max-height: unset;
width: 240px;
height: 360px;
}
@include from($tabletSmall) {
margin: unset;
display: unset;
max-width: unset;
max-height: unset;
width: 240px;
height: 360px;
}
@include from($desktopSmall) {
height: 720px;
width: 480px;
}
@include from($desktopSmall) {
height: 720px;
width: 480px;
}
}
.buttonContainer {
display: flex;
flex-direction: row;
gap: var(--collection-page_content_button-gap);
display: flex;
flex-direction: row;
gap: var(--collection-page_content_button-gap);
}
.buttonContainer>* {
width: 1px;
flex-grow: 1;
.buttonContainer > * {
width: 1px;
flex-grow: 1;
}
.authorsList {
list-style: none;
margin: 0;
padding: 0;
list-style: none;
margin: 0;
padding: 0;
}
.authorContainer {
padding: var(--collection-page_author_padding);
background-color: var(--surface_primary_emphasis-none);
border-radius: var(--collection-page_author_corner-radius);
display: grid;
padding: var(--collection-page_author_padding);
background-color: var(--surface_primary_emphasis-none);
border-radius: var(--collection-page_author_corner-radius);
display: grid;
grid-template-columns: min-content 1fr;
grid-template-rows: repeat(3, min-content);
grid-template-columns: min-content 1fr;
grid-template-rows: repeat(3, min-content);
@include from($tabletSmall) {
grid-template-columns: min-content 1fr min-content;
grid-column-gap: var(--collection-page_description_padding-top);
}
@include from($tabletSmall) {
grid-template-columns: min-content 1fr min-content;
grid-column-gap: var(--collection-page_description_padding-top);
}
}
.authorImage {
height: var(--collection-page_author_avatar_size);
width: var(--collection-page_author_avatar_size);
align-self: center;
border-radius: 50%;
overflow: hidden;
border-style: solid;
border-width: var(--collection-page_author_avatar_border-width);
margin-right: var(--collection-page_author_details_gap);
height: var(--collection-page_author_avatar_size);
width: var(--collection-page_author_avatar_size);
align-self: center;
border-radius: 50%;
overflow: hidden;
border-style: solid;
border-width: var(--collection-page_author_avatar_border-width);
margin-right: var(--collection-page_author_details_gap);
grid-row: 1;
grid-column: 1;
grid-row: 1;
grid-column: 1;
@include from($tabletSmall) {
grid-row: 1;
grid-column: 1;
}
@include from($tabletSmall) {
grid-row: 1;
grid-column: 1;
}
}
.authorImage img {
height: 100%;
width: 100%;
height: 100%;
width: 100%;
}
.authorMetaData {
height: max-content;
align-self: center;
height: max-content;
align-self: center;
grid-row: 1;
grid-column: 2;
grid-row: 1;
grid-column: 2;
@include from($desktopSmall) {
grid-row: 1;
grid-column: 2;
}
@include from($desktopSmall) {
grid-row: 1;
grid-column: 2;
}
}
.authorName {
margin: 0;
color: var(--collection-page_author_name_color);
margin: 0;
color: var(--collection-page_author_name_color);
}
.authorArticles {
margin: 0;
color: var(--collection-page_author_count-articles_color);
margin: 0;
color: var(--collection-page_author_count-articles_color);
}
.authorWordCount,
.authorMetaSeperatorDot {
margin: 0;
color: var(--collection-page_author_count-words_color);
margin: 0;
color: var(--collection-page_author_count-words_color);
}
.viewProfileBtn {
height: fit-content;
align-self: center;
height: fit-content;
align-self: center;
grid-row: 3;
grid-column: 1 / 3;
grid-row: 3;
grid-column: 1 / 3;
@include from($tabletSmall) {
grid-row: 1;
grid-column: 3;
}
@include from($tabletSmall) {
grid-row: 1;
grid-column: 3;
}
}
.authorDescription {
padding-top: var(--collection-page_author_description_padding-top);
padding-bottom: var(--collection-page_title_padding-bottom);
padding-top: var(--collection-page_author_description_padding-top);
padding-bottom: var(--collection-page_title_padding-bottom);
grid-row: 2;
grid-column: 1 / 3;
grid-row: 2;
grid-column: 1 / 3;
@include from($tabletSmall) {
padding-bottom: unset;
grid-row: 2;
grid-column: 1 / span 3;
}
@include from($tabletSmall) {
padding-bottom: unset;
grid-row: 2;
grid-column: 1 / span 3;
}
}

View File

@@ -1,291 +1,294 @@
@import "../tokens/utils";
@import "../tokens/breakpoints";
.pricingCardContainerLi {
display: block;
padding: var(--section-ver-padding) var(--section-hor-padding);
border-radius: var(--pricing-container-radius);
background: var(--pricing-container_default);
transition: background var(--color-transition-time)
var(--color-transition-ease),
color var(--color-transition-time) var(--color-transition-ease);
position: relative;
}
.pricingCardContainerLi.highlighted {
background: var(--pricing-container_highlighted);
}
@include from($desktop) {
.pricingCardContainerLi {
width: calc(100% / 3 - var(--pricing-container-radius));
}
.pricingCardContainerLi.highlighted {
width: calc(100% / 3);
}
}
.pricingCardContainer {
display: flex;
flex-direction: column;
gap: var(--section-gap);
text-align: center;
}
.topTextContainer {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--section-gap);
}
@include from($tablet) {
.topTextContainer {
flex-direction: row;
}
.topTextContainer > div {
width: 50%;
}
.topTextContainer > div:first-child {
text-align: start;
}
.topTextContainer > div:last-child {
text-align: end;
}
}
@include from($desktop) {
.topTextContainer {
flex-direction: column;
}
.topTextContainer > div {
width: 100%;
}
.topTextContainer > div:first-child,
.topTextContainer > div:last-child {
text-align: center;
}
}
.saleTag {
position: absolute;
height: var(--sale-badge-size);
width: var(--sale-badge-size);
top: var(--sale-badge-offset);
right: var(--sale-badge-offset);
transform: rotate(var(--sale-badge-rotation));
}
.pricingTitle {
color: var(--on-dark-emphasis-high);
margin: 0;
margin-bottom: var(--spc-1x);
}
.pricingDescription {
color: var(--on-dark-emphasis-medium);
margin: 0;
}
.pricingPrice {
color: var(--on-dark-emphasis-high);
margin: 0;
margin-bottom: var(--spc-1x);
}
.enterpriseToggleContainer {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 1fr auto 1fr;
align-items: center;
}
.enterpriseToggleText {
margin: 0;
color: var(--slate-300);
padding: 0 var(--spc-2x);
}
@include from($desktop) {
.enterpriseToggleText {
padding: 0 var(--spc-1x);
}
}
.enterpriseToggleText {
text-align: right;
}
.enterpriseToggleText + .enterpriseToggleText {
text-align: left;
}
.enterpriseToggle:not(:checked) + .enterpriseToggleText {
color: var(--slate-050);
}
.enterpriseToggle:checked + .enterpriseToggleText + .enterpriseToggleText {
color: var(--slate-050);
}
.individualText {
order: -1;
}
input[type="checkbox"].enterpriseToggle {
appearance: none;
width: var(--pricing-toggle-width);
height: var(--pricing-toggle-height);
position: relative;
border-radius: 50px;
box-sizing: content-box;
cursor: pointer;
transition: background 150ms ease-in-out;
background: var(--pricing-toggle-track-color);
}
input[type="checkbox"].enterpriseToggle:hover {
background: var(--pricing-toggle-track-color_hovered);
}
input[type="checkbox"].enterpriseToggle:active {
background: var(--pricing-toggle-track-color_pressed);
}
/* Thumb */
input[type="checkbox"].enterpriseToggle::before {
top: 50%;
left: var(--pricing-toggle-gap);
transform: translateY(-50%);
transition: left 150ms ease-in-out, background-color 150ms ease-in-out,
height 150ms ease-in-out, width 150ms ease-in-out;
content: " ";
width: var(--pricing-toggle-thumb-size);
height: var(--pricing-toggle-thumb-size);
background: var(--slate-200);
position: absolute;
border-radius: 50px;
}
input[type="checkbox"].enterpriseToggle:hover::before {
width: var(--pricing-toggle-thumb-size_hovered);
height: var(--pricing-toggle-thumb-size_hovered);
}
input[type="checkbox"].enterpriseToggle:active::before {
width: var(--pricing-toggle-thumb-size_pressed);
height: var(--pricing-toggle-thumb-size_pressed);
}
input[type="checkbox"].enterpriseToggle:hover::before {
left: var(--pricing-toggle-gap_hovered);
}
input[type="checkbox"].enterpriseToggle:active::before {
left: var(--pricing-toggle-gap_pressed);
}
input[type="checkbox"].enterpriseToggle:checked {
background: var(--slate-500);
}
input[type="checkbox"].enterpriseToggle:checked::before {
left: calc(
100% - var(--pricing-toggle-thumb-size) - var(--pricing-toggle-gap)
);
}
input[type="checkbox"].enterpriseToggle:checked:hover::before {
left: calc(
100% - var(--pricing-toggle-thumb-size_hovered) -
var(--pricing-toggle-gap_hovered)
);
}
input[type="checkbox"].enterpriseToggle:checked:active::before {
left: calc(
100% - var(--pricing-toggle-thumb-size_pressed) -
var(--pricing-toggle-gap_pressed)
);
}
.pricingEnterpriseExplainer {
color: var(--on-dark-emphasis-medium);
margin: 0;
opacity: 0;
transition: opacity 300ms ease-in-out;
}
.divider {
display: none;
border: none;
border-top: 2px dotted #607090;
}
@include from($tablet) {
.divider {
display: block;
}
}
.listItems {
text-align: start;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--section-gap);
}
@include from($tablet) {
.listItems {
flex-direction: row;
}
.listItems > ul {
width: 50%;
}
}
@include from($desktop) {
.listItems {
flex-direction: column;
}
.listItems > ul {
width: 100%;
}
}
.listItemBullet {
height: var(--bullet-list-item-icon-size);
width: var(--bullet-list-item-icon-size);
margin-right: var(--spc-1x);
}
.starPoints,
.bulletPoints {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--bullet-list-item-gap);
}
.starPoints > li,
.bulletPoints > li {
display: flex;
align-items: center;
}
@include until($tablet) {
.removeOnMobile {
display: none;
}
}
@import "../tokens/utils";
@import "../tokens/breakpoints";
.pricingCardContainerLi {
display: block;
padding: var(--section-ver-padding) var(--section-hor-padding);
border-radius: var(--pricing-container-radius);
background: var(--pricing-container_default);
transition:
background var(--color-transition-time) var(--color-transition-ease),
color var(--color-transition-time) var(--color-transition-ease);
position: relative;
}
.pricingCardContainerLi.highlighted {
background: var(--pricing-container_highlighted);
}
@include from($desktop) {
.pricingCardContainerLi {
width: calc(100% / 3 - var(--pricing-container-radius));
}
.pricingCardContainerLi.highlighted {
width: calc(100% / 3);
}
}
.pricingCardContainer {
display: flex;
flex-direction: column;
gap: var(--section-gap);
text-align: center;
}
.topTextContainer {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--section-gap);
}
@include from($tablet) {
.topTextContainer {
flex-direction: row;
}
.topTextContainer > div {
width: 50%;
}
.topTextContainer > div:first-child {
text-align: start;
}
.topTextContainer > div:last-child {
text-align: end;
}
}
@include from($desktop) {
.topTextContainer {
flex-direction: column;
}
.topTextContainer > div {
width: 100%;
}
.topTextContainer > div:first-child,
.topTextContainer > div:last-child {
text-align: center;
}
}
.saleTag {
position: absolute;
height: var(--sale-badge-size);
width: var(--sale-badge-size);
top: var(--sale-badge-offset);
right: var(--sale-badge-offset);
transform: rotate(var(--sale-badge-rotation));
}
.pricingTitle {
color: var(--on-dark-emphasis-high);
margin: 0;
margin-bottom: var(--spc-1x);
}
.pricingDescription {
color: var(--on-dark-emphasis-medium);
margin: 0;
}
.pricingPrice {
color: var(--on-dark-emphasis-high);
margin: 0;
margin-bottom: var(--spc-1x);
}
.enterpriseToggleContainer {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 1fr auto 1fr;
align-items: center;
}
.enterpriseToggleText {
margin: 0;
color: var(--slate-300);
padding: 0 var(--spc-2x);
}
@include from($desktop) {
.enterpriseToggleText {
padding: 0 var(--spc-1x);
}
}
.enterpriseToggleText {
text-align: right;
}
.enterpriseToggleText + .enterpriseToggleText {
text-align: left;
}
.enterpriseToggle:not(:checked) + .enterpriseToggleText {
color: var(--slate-050);
}
.enterpriseToggle:checked + .enterpriseToggleText + .enterpriseToggleText {
color: var(--slate-050);
}
.individualText {
order: -1;
}
input[type="checkbox"].enterpriseToggle {
appearance: none;
width: var(--pricing-toggle-width);
height: var(--pricing-toggle-height);
position: relative;
border-radius: 50px;
box-sizing: content-box;
cursor: pointer;
transition: background 150ms ease-in-out;
background: var(--pricing-toggle-track-color);
}
input[type="checkbox"].enterpriseToggle:hover {
background: var(--pricing-toggle-track-color_hovered);
}
input[type="checkbox"].enterpriseToggle:active {
background: var(--pricing-toggle-track-color_pressed);
}
/* Thumb */
input[type="checkbox"].enterpriseToggle::before {
top: 50%;
left: var(--pricing-toggle-gap);
transform: translateY(-50%);
transition:
left 150ms ease-in-out,
background-color 150ms ease-in-out,
height 150ms ease-in-out,
width 150ms ease-in-out;
content: " ";
width: var(--pricing-toggle-thumb-size);
height: var(--pricing-toggle-thumb-size);
background: var(--slate-200);
position: absolute;
border-radius: 50px;
}
input[type="checkbox"].enterpriseToggle:hover::before {
width: var(--pricing-toggle-thumb-size_hovered);
height: var(--pricing-toggle-thumb-size_hovered);
}
input[type="checkbox"].enterpriseToggle:active::before {
width: var(--pricing-toggle-thumb-size_pressed);
height: var(--pricing-toggle-thumb-size_pressed);
}
input[type="checkbox"].enterpriseToggle:hover::before {
left: var(--pricing-toggle-gap_hovered);
}
input[type="checkbox"].enterpriseToggle:active::before {
left: var(--pricing-toggle-gap_pressed);
}
input[type="checkbox"].enterpriseToggle:checked {
background: var(--slate-500);
}
input[type="checkbox"].enterpriseToggle:checked::before {
left: calc(
100% - var(--pricing-toggle-thumb-size) - var(--pricing-toggle-gap)
);
}
input[type="checkbox"].enterpriseToggle:checked:hover::before {
left: calc(
100% - var(--pricing-toggle-thumb-size_hovered) -
var(--pricing-toggle-gap_hovered)
);
}
input[type="checkbox"].enterpriseToggle:checked:active::before {
left: calc(
100% - var(--pricing-toggle-thumb-size_pressed) -
var(--pricing-toggle-gap_pressed)
);
}
.pricingEnterpriseExplainer {
color: var(--on-dark-emphasis-medium);
margin: 0;
opacity: 0;
transition: opacity 300ms ease-in-out;
}
.divider {
display: none;
border: none;
border-top: 2px dotted #607090;
}
@include from($tablet) {
.divider {
display: block;
}
}
.listItems {
text-align: start;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--section-gap);
}
@include from($tablet) {
.listItems {
flex-direction: row;
}
.listItems > ul {
width: 50%;
}
}
@include from($desktop) {
.listItems {
flex-direction: column;
}
.listItems > ul {
width: 100%;
}
}
.listItemBullet {
height: var(--bullet-list-item-icon-size);
width: var(--bullet-list-item-icon-size);
margin-right: var(--spc-1x);
}
.starPoints,
.bulletPoints {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: var(--bullet-list-item-gap);
}
.starPoints > li,
.bulletPoints > li {
display: flex;
align-items: center;
}
@include until($tablet) {
.removeOnMobile {
display: none;
}
}

View File

@@ -1,77 +1,76 @@
.quoteContainer {
padding: var(--section-ver-padding) var(--section-hor-padding);
text-align: center;
position: relative;
z-index: 1;
margin: 0;
}
.quoteText {
margin: 0;
position: relative;
}
.quoteText::after,
.quoteText::before {
content: " ";
position: absolute;
height: var(--quote-marks-height_prominent);
aspect-ratio: var(--quote-mark-aspect-ratio);
background-image: url("../icons/big_quotation_mark.svg");
background-repeat: no-repeat;
background-size: contain;
z-index: -1;
opacity: var(--quote-mark-opacity);
}
// Top right corner quote
.quoteText::after {
top: calc(0px - var(--quote-mark-offset_prominent));
right: calc(0px - var(--quote-mark-offset_prominent));
}
// Bottom left corner quote
.quoteText::before {
bottom: calc(0px - var(--quote-mark-offset_prominent));
left: calc(0px - var(--quote-mark-offset_prominent));
transform: rotate(180deg);
}
.quoteAvatar {
margin-top: var(--section-gap);
margin-bottom: var(--spc-1x);
margin-left: auto;
margin-right: auto;
height: var(--quote-avatar-size_prominent);
width: var(--quote-avatar-size_prominent);
border-radius: var(--corner-radius-circular);
}
.quotePersonName {
margin: 0;
color: var(--on-dark-emphasis-high);
text-decoration-line: underline !important;
text-decoration-color: var(--quote-arrow-link-color) !important;
text-decoration-thickness: 1.5px !important;
position: relative;
display: inline-block;
}
.quotePersonName::before {
content: " ";
position: absolute;
right: calc(-4px - 0.5rem);
top: 50%;
transform: translateY(-50%);
height: 0.5rem;
width: 0.5rem;
background-image: url("../icons/link_arrow.svg");
background-repeat: no-repeat;
}
.quotePersonTitle {
margin-top: 4px;
margin-bottom: 0;
color: var(--on-dark-emphasis-medium);
}
.quoteContainer {
padding: var(--section-ver-padding) var(--section-hor-padding);
text-align: center;
position: relative;
z-index: 1;
margin: 0;
}
.quoteText {
margin: 0;
position: relative;
}
.quoteText::after,
.quoteText::before {
content: " ";
position: absolute;
height: var(--quote-marks-height_prominent);
aspect-ratio: var(--quote-mark-aspect-ratio);
background-image: url("../icons/big_quotation_mark.svg");
background-repeat: no-repeat;
background-size: contain;
z-index: -1;
opacity: var(--quote-mark-opacity);
}
// Top right corner quote
.quoteText::after {
top: calc(0px - var(--quote-mark-offset_prominent));
right: calc(0px - var(--quote-mark-offset_prominent));
}
// Bottom left corner quote
.quoteText::before {
bottom: calc(0px - var(--quote-mark-offset_prominent));
left: calc(0px - var(--quote-mark-offset_prominent));
transform: rotate(180deg);
}
.quoteAvatar {
margin-top: var(--section-gap);
margin-bottom: var(--spc-1x);
margin-left: auto;
margin-right: auto;
height: var(--quote-avatar-size_prominent);
width: var(--quote-avatar-size_prominent);
border-radius: var(--corner-radius-circular);
}
.quotePersonName {
margin: 0;
color: var(--on-dark-emphasis-high);
text-decoration-line: underline !important;
text-decoration-color: var(--quote-arrow-link-color) !important;
text-decoration-thickness: 1.5px !important;
position: relative;
display: inline-block;
}
.quotePersonName::before {
content: " ";
position: absolute;
right: calc(-4px - 0.5rem);
top: 50%;
transform: translateY(-50%);
height: 0.5rem;
width: 0.5rem;
background-image: url("../icons/link_arrow.svg");
background-repeat: no-repeat;
}
.quotePersonTitle {
margin-top: 4px;
margin-bottom: 0;
color: var(--on-dark-emphasis-medium);
}

View File

@@ -1,84 +1,84 @@
.quoteCardContainerLi {
display: block;
}
.quoteCardContainer {
display: flex;
flex-direction: column;
gap: var(--quote-spacing);
padding: var(--quote-spacing);
position: relative;
overflow: hidden;
z-index: 1;
background: var(--slate-800);
transition: background var(--color-transition-time)
var(--color-transition-ease);
border-radius: var(--quote-corner-radius);
}
// Top right corner quote
.quoteCardContainer::after {
content: " ";
position: absolute;
height: var(--quote-marks-height_contained);
aspect-ratio: var(--quote-mark-aspect-ratio);
background-image: url("../icons/big_quotation_mark.svg");
background-repeat: no-repeat;
background-size: contain;
z-index: -1;
opacity: var(--quote-mark-opacity);
top: calc(0px - var(--quote-mark-offset));
right: calc(0px - var(--quote-mark-offset));
}
.quoteText {
margin: 0;
}
.personDetailsContainer picture {
height: var(--quote-avatar-size);
width: var(--quote-avatar-size);
}
.personAvatar {
height: var(--quote-avatar-size);
width: var(--quote-avatar-size);
border-radius: var(--corner-radius-circular);
}
.personDetailsContainer {
display: flex;
flex-wrap: nowrap;
gap: var(--spc-2x);
align-items: center;
}
.personTextContainer {
}
.personName {
margin: 0;
text-decoration-line: underline !important;
text-decoration-color: var(--quote-arrow-link-color) !important;
text-decoration-thickness: 1.5px !important;
position: relative;
width: fit-content;
color: var(--on-dark-emphasis-high);
}
.personName::before {
content: " ";
position: absolute;
right: calc(-4px - 0.5rem);
top: 50%;
transform: translateY(-50%);
height: 0.5rem;
width: 0.5rem;
background-image: url("../icons/link_arrow.svg");
background-repeat: no-repeat;
}
.personTitle {
margin: 0;
color: var(--on-dark-emphasis-medium);
}
.quoteCardContainerLi {
display: block;
}
.quoteCardContainer {
display: flex;
flex-direction: column;
gap: var(--quote-spacing);
padding: var(--quote-spacing);
position: relative;
overflow: hidden;
z-index: 1;
background: var(--slate-800);
transition: background var(--color-transition-time)
var(--color-transition-ease);
border-radius: var(--quote-corner-radius);
}
// Top right corner quote
.quoteCardContainer::after {
content: " ";
position: absolute;
height: var(--quote-marks-height_contained);
aspect-ratio: var(--quote-mark-aspect-ratio);
background-image: url("../icons/big_quotation_mark.svg");
background-repeat: no-repeat;
background-size: contain;
z-index: -1;
opacity: var(--quote-mark-opacity);
top: calc(0px - var(--quote-mark-offset));
right: calc(0px - var(--quote-mark-offset));
}
.quoteText {
margin: 0;
}
.personDetailsContainer picture {
height: var(--quote-avatar-size);
width: var(--quote-avatar-size);
}
.personAvatar {
height: var(--quote-avatar-size);
width: var(--quote-avatar-size);
border-radius: var(--corner-radius-circular);
}
.personDetailsContainer {
display: flex;
flex-wrap: nowrap;
gap: var(--spc-2x);
align-items: center;
}
.personTextContainer {
}
.personName {
margin: 0;
text-decoration-line: underline !important;
text-decoration-color: var(--quote-arrow-link-color) !important;
text-decoration-thickness: 1.5px !important;
position: relative;
width: fit-content;
color: var(--on-dark-emphasis-high);
}
.personName::before {
content: " ";
position: absolute;
right: calc(-4px - 0.5rem);
top: 50%;
transform: translateY(-50%);
height: 0.5rem;
width: 0.5rem;
background-image: url("../icons/link_arrow.svg");
background-repeat: no-repeat;
}
.personTitle {
margin: 0;
color: var(--on-dark-emphasis-medium);
}

View File

@@ -1,72 +1,74 @@
@import "../tokens/utils";
@import "../tokens/breakpoints.scss";
.quotesContainer {
column-count: 1;
column-gap: var(--quote-grid-gap);
margin: 0;
padding: 0;
list-style: none;
margin-bottom: 5rem;
}
.quotesContainer > * {
break-inside: avoid;
margin-bottom: var(--quote-grid-gap);
}
@include from($tablet) {
.quotesContainer {
column-count: 2;
}
}
@include from($desktop) {
.quotesContainer {
column-count: 3;
}
}
.quotesAndButtonsContainer {
position: relative;
display: flex;
flex-direction: column;
}
.showMoreLessContainer {
order: -1;
flex-grow: 0;
position: sticky;
z-index: 2;
top: 100vh;
pointer-events: none;
}
.fullHeightSizer {
position: absolute;
top: -100vh;
left: 0;
height: 100vh;
width: 100%;
pointer-events: none;
}
.showMoreButton, .showLessButton {
position: absolute !important;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
pointer-events: all;
}
.quotesScrim {
background: var(--section-background);
transition: background var(--color-transition-time) var(--color-transition-ease);
height: 100%;
position: absolute;
inset: 0;
z-index: 1;
mask-image: linear-gradient(0deg, black 0%, transparent 50%);
-webkit-mask-image: linear-gradient(0deg, black 0%, transparent 50%);
pointer-events: none;
}
@import "../tokens/utils";
@import "../tokens/breakpoints.scss";
.quotesContainer {
column-count: 1;
column-gap: var(--quote-grid-gap);
margin: 0;
padding: 0;
list-style: none;
margin-bottom: 5rem;
}
.quotesContainer > * {
break-inside: avoid;
margin-bottom: var(--quote-grid-gap);
}
@include from($tablet) {
.quotesContainer {
column-count: 2;
}
}
@include from($desktop) {
.quotesContainer {
column-count: 3;
}
}
.quotesAndButtonsContainer {
position: relative;
display: flex;
flex-direction: column;
}
.showMoreLessContainer {
order: -1;
flex-grow: 0;
position: sticky;
z-index: 2;
top: 100vh;
pointer-events: none;
}
.fullHeightSizer {
position: absolute;
top: -100vh;
left: 0;
height: 100vh;
width: 100%;
pointer-events: none;
}
.showMoreButton,
.showLessButton {
position: absolute !important;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
pointer-events: all;
}
.quotesScrim {
background: var(--section-background);
transition: background var(--color-transition-time)
var(--color-transition-ease);
height: 100%;
position: absolute;
inset: 0;
z-index: 1;
mask-image: linear-gradient(0deg, black 0%, transparent 50%);
-webkit-mask-image: linear-gradient(0deg, black 0%, transparent 50%);
pointer-events: none;
}

View File

@@ -1,105 +1,108 @@
@import "../tokens/utils";
@import "../tokens/breakpoints";
.book-section {
padding: var(--section-ver-padding) 0;
text-align: center;
transition: background-color var(--color-transition-time) var(--color-transition-ease);
will-change: transition;
}
.book-section > :not([data-no-horiz-pad]) {
padding: 0 var(--section-ver-padding);
}
.book-section > :not([data-no-max-width]) {
max-width: var(--max-width_small);
margin-left: auto;
margin-right: auto;
}
.book-title {
background: var(--text-gradient);
transition: background var(--color-transition-time) var(--color-transition-ease);
will-change: transition;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin: 0 auto;
line-height: normal !important;
}
.book-subtitle {
color: var(--on-dark-emphasis-medium);
margin: var(--section-gap) 0;
}
.book-bio {
color: var(--on-dark-emphasis-high)
}
.book-ctas {
display: grid;
grid-template-columns: repeat(auto-fit, 100%);
justify-content: center;
gap: var(--section-gap);
margin-left: auto;
margin-right: auto;
margin-top: var(--spc-6x);
margin-bottom: var(--section-gap);
@include from($tablet) {
grid-template-columns: repeat(auto-fit, minmax(min-content, 280px));
}
}
.book-coming-soon {
color: var(--on-dark-emphasis-medium);
}
.section-metrics-main-container {
display: flex;
justify-content: space-around;
}
.section-metric-container {
padding: var(--spc-2x) var(--spc-1x);
@include from($desktop) {
padding: var(--spc-4x) var(--spc-2x);
}
}
.section-metric-text {
margin: 0;
}
.text-style-main-text-only, .section-metric-asterisk {
background: var(--text-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.section-metric-subtitle {
color: var(--on-dark-emphasis-medium);
display: block;
}
.text-style-main-text {
position: relative;
display: inline-block;
}
.section-metric-asterisk {
position: absolute;
left: 100%;
top: calc(0px - var(--spc-1x));
}
.section-chip-group-container {
margin: var(--section-gap) auto;
}
.section-disclaimer {
margin: 0;
color: var(--on-dark-emphasis-medium);
}
@import "../tokens/utils";
@import "../tokens/breakpoints";
.book-section {
padding: var(--section-ver-padding) 0;
text-align: center;
transition: background-color var(--color-transition-time)
var(--color-transition-ease);
will-change: transition;
}
.book-section > :not([data-no-horiz-pad]) {
padding: 0 var(--section-ver-padding);
}
.book-section > :not([data-no-max-width]) {
max-width: var(--max-width_small);
margin-left: auto;
margin-right: auto;
}
.book-title {
background: var(--text-gradient);
transition: background var(--color-transition-time)
var(--color-transition-ease);
will-change: transition;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin: 0 auto;
line-height: normal !important;
}
.book-subtitle {
color: var(--on-dark-emphasis-medium);
margin: var(--section-gap) 0;
}
.book-bio {
color: var(--on-dark-emphasis-high);
}
.book-ctas {
display: grid;
grid-template-columns: repeat(auto-fit, 100%);
justify-content: center;
gap: var(--section-gap);
margin-left: auto;
margin-right: auto;
margin-top: var(--spc-6x);
margin-bottom: var(--section-gap);
@include from($tablet) {
grid-template-columns: repeat(auto-fit, minmax(min-content, 280px));
}
}
.book-coming-soon {
color: var(--on-dark-emphasis-medium);
}
.section-metrics-main-container {
display: flex;
justify-content: space-around;
}
.section-metric-container {
padding: var(--spc-2x) var(--spc-1x);
@include from($desktop) {
padding: var(--spc-4x) var(--spc-2x);
}
}
.section-metric-text {
margin: 0;
}
.text-style-main-text-only,
.section-metric-asterisk {
background: var(--text-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.section-metric-subtitle {
color: var(--on-dark-emphasis-medium);
display: block;
}
.text-style-main-text {
position: relative;
display: inline-block;
}
.section-metric-asterisk {
position: absolute;
left: 100%;
top: calc(0px - var(--spc-1x));
}
.section-chip-group-container {
margin: var(--section-gap) auto;
}
.section-disclaimer {
margin: 0;
color: var(--on-dark-emphasis-medium);
}

View File

@@ -1,86 +1,88 @@
@import "../tokens/utils";
@import "../tokens/breakpoints";
.signUpFormContainer {
display: flex;
flex-direction: column;
gap: 24px;
}
@include from($desktop) {
.signUpFormContainer {
flex-direction: row;
align-items: flex-end;
gap: 0;
}
}
.inputContainer {
display: inline-block;
text-align: start;
}
@include from($desktop) {
.inputContainer {
flex-basis: 100%;
}
}
.emailInput {
}
@include from($desktop) {
.emailInput {
margin-left: var(--section-gap);
margin-right: var(--sign-up-btn-gap);
}
}
.inputLabel {
margin-left: var(--spc-2x);
margin-bottom: var(--spc-1x);
display: block;
}
.input {
box-sizing: border-box;
width: 100%;
border-style: solid;
border-radius: var(--form-border-radius);
border-width: var(--form-border-width);
border-color: var(--outline);
background: transparent;
color: white;
padding: var(--form-padding-vertical) 0 var(--form-padding-vertical)
var(--form-padding-horizontal);
transition: background var(--btn-state-transition-duration)
var(--btn-state-transition-easing), border-color var(--btn-state-transition-duration)
var(--btn-state-transition-easing);
}
.input:hover {
background: var(--slate-800);
}
.input:focus {
background: var(--slate-700);
border-color: var(--outline-focused);
}
@include until($desktop) {
.input {
line-height: 1.5rem !important;
}
}
.signUpButton {
flex-shrink: 0;
border-radius: var(--form-border-radius) !important;
height: fit-content;
}
@include from($desktop) {
.hideOnDesktop {
display: none;
}
}
@import "../tokens/utils";
@import "../tokens/breakpoints";
.signUpFormContainer {
display: flex;
flex-direction: column;
gap: 24px;
}
@include from($desktop) {
.signUpFormContainer {
flex-direction: row;
align-items: flex-end;
gap: 0;
}
}
.inputContainer {
display: inline-block;
text-align: start;
}
@include from($desktop) {
.inputContainer {
flex-basis: 100%;
}
}
.emailInput {
}
@include from($desktop) {
.emailInput {
margin-left: var(--section-gap);
margin-right: var(--sign-up-btn-gap);
}
}
.inputLabel {
margin-left: var(--spc-2x);
margin-bottom: var(--spc-1x);
display: block;
}
.input {
box-sizing: border-box;
width: 100%;
border-style: solid;
border-radius: var(--form-border-radius);
border-width: var(--form-border-width);
border-color: var(--outline);
background: transparent;
color: white;
padding: var(--form-padding-vertical) 0 var(--form-padding-vertical)
var(--form-padding-horizontal);
transition:
background var(--btn-state-transition-duration)
var(--btn-state-transition-easing),
border-color var(--btn-state-transition-duration)
var(--btn-state-transition-easing);
}
.input:hover {
background: var(--slate-800);
}
.input:focus {
background: var(--slate-700);
border-color: var(--outline-focused);
}
@include until($desktop) {
.input {
line-height: 1.5rem !important;
}
}
.signUpButton {
flex-shrink: 0;
border-radius: var(--form-border-radius) !important;
height: fit-content;
}
@include from($desktop) {
.hideOnDesktop {
display: none;
}
}

View File

@@ -1,13 +1,13 @@
import { useMemo } from "preact/hooks";
import { useCSSPropertyValue } from "../../hooks/use-css-property-value";
import { RepeatBackground } from "../shared/repeat-background";
export const ForestBackground = () => {
const forestBackgroundColor = useCSSPropertyValue('--ecos-700', "#366E47");
const svg = useMemo(() => {
return `<svg class="nofill" data-repeated="true" xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 3022 1920' ><g><path fill='${forestBackgroundColor}' fill-rule='evenodd' d='M0 0v1342l8 5c1 2 10 6 20 9 15 5 20 13 41 69 14 35 29 64 31 68 32 60-76 427-76 427h26c27-81 52-191 73-285 19-82 35-150 45-174 15-27 75-97 81-93 3 2 12-4 21-13 8-9 20-17 27-18 6-1 17-8 23-15 9-11 11-12 11-3 0 6 5 10 12 10a539 539 0 0 1 121 257c18 107 21 121 28 118 8-2 8-56 0-201-6-100-6-100 8-125 13-23 28-32 32-20 1 3 10 9 20 13 23 11 25 25 30 169 4 101 60 258 60 258h20s-53-175-58-241c-7-88-2-182 7-198 7-15 12-18 22-14 7 2 18 8 23 13 10 8 11 8 11-1 0-8 11-16 33-24 22 8 33 16 33 24 0 9 1 9 11 1 5-5 16-11 23-13 10-4 15-1 22 14 9 16 9 28 3 111-8 96 17 410 45 410 10 10-26-151-19-340 5-144 7-158 30-169 10-4 19-10 20-13 4-12 19-3 32 20 14 25 14 25 8 125-8 145 20 385 28 388 22 3-18-198 0-305 12-71 34-129 77-201 23-40 36-56 44-56 7 0 12-4 12-10 0-9 2-8 11 3 6 7 17 14 23 15 7 1 19 9 27 18 9 9 18 15 21 13 6-4 66 66 81 93 10 24 26 92 45 174 21 94 46 204 73 285h26s-108-367-76-427c2-4 18-33 31-68 21-56 26-64 41-69 10-3 19-7 20-9l15-9c7-4 12-13 12-22 1-19 8-27 14-17 3 5 8 7 13 4 12-8 35-9 35-1 0 4 2 4 7-1 9-9 16-9 16 1 0 5 4 8 8 8 27-1 45 3 44 9-1 3 9 9 22 12 42 11 66 46 94 137 26 87 169 427 169 427l35 4s-119-279-158-395c-29-88-32-103-30-137 2-39 15-58 33-51 7 3 8-1 6-15-3-18 0-19 19-4 14 12 35 17 35 8 0-5 5-11 11-15 9-6 13-5 19 2 4 5 12 8 18 6 5-1 11 0 12 3s15 9 31 13c29 7 30 8 31 31 18 241 217 558 217 558h20s-224-376-201-573c8-13 21-11 16 3-2 5 1 8 6 8 4 0 8-2 8-5 0-7 29-19 36-15 3 2 8-2 12-9 6-8 12-11 21-8 9 2 14 1 14-5 0-5 1-6 7 0 13 13 22 7 26-16 2-16 6-22 11-20 4 1 14-1 21-5 8-4 16-7 19-6 2 0 6-3 8-8 1-5 7-9 11-9h3c4 8 10 9 26 8 12-1 18 0 14 3-10 7 1 17 22 20 8 1 15 6 17 13 1 6 7 17 14 24 10 12 14 13 21 7 6-5 10-5 15 2 5 8 17 15 33 20 23 8 35 17 35 25 0 9 1 9 11 1 5-5 16-11 23-13 10-4 15-1 22 14 9 16 21 175 21 307 0 127-30 169-11 169 49 0 13-421 49-464 6-7 19-10 20-13 4-12 19-3 32 20 14 25 11 76 11 131 0 70-11 277 23 394h19s-33-224-14-338c12-72 31-108 74-180 23-40 36-56 44-56 7 0 12-4 12-10 0-9 2-8 11 3 6 7 17 14 23 15 7 1 19 9 27 18 9 9 18 15 21 13 6-4 66 66 81 93 10 24 26 92 45 174 21 94 46 204 73 285h26s-108-367-76-427c2-4 18-33 31-68 21-56 26-64 41-69 10-3 19-7 20-9l8-5V0H0Zm397 1334c-5 2-3 7 6 20 9 14 13 16 17 10 8-12 6-17-3-13-5 2-9-1-10-8s-6-11-10-9Zm58 18c0 4-4 5-8 3-5-1-11 4-15 15-7 15-5 21 13 57l21 41v-57c0-32-2-59-5-61-4-2-6-1-6 2Zm-368 15c-12 7-12 7 4 51l16 44v-48c0-27-1-50-4-51-2-1-10 1-16 4Zm916-33c5 2 3 7-6 20-9 14-13 16-17 10-8-12-6-17 3-13 5 2 9-1 10-8s6-11 10-9Zm-58 18c0 4 4 5 8 3 5-1 11 4 15 15 7 15 5 21-13 57l-21 41s3-47 0-69c-4-29 2-47 5-49 4-2 6-1 6 2Zm368 15c12 7 12 7-4 51l-16 44v-48c0-27 1-50 4-51 2-1 10 1 16 4Zm1306-13c9-13 11-18 6-20-4-2-9 2-10 9s-5 10-10 8c-9-4-11 1-3 13 4 6 8 4 17-10Zm-44 1c-4 2-8 1-8-3 0-3-2-4-6-2-3 2-5 29-5 61v57l21-41c18-36 20-42 13-57-4-11-10-16-15-15Zm356 63c16-44 16-44 4-51-6-3-14-5-16-4-3 1-4 24-4 51v48l16-44Z' clip-rule='evenodd'></path></g></svg>`
}, [forestBackgroundColor]);
return <RepeatBackground aspectRatio={"3022/1920"} svg={svg} />;
};
import { useMemo } from "preact/hooks";
import { useCSSPropertyValue } from "../../hooks/use-css-property-value";
import { RepeatBackground } from "../shared/repeat-background";
export const ForestBackground = () => {
const forestBackgroundColor = useCSSPropertyValue("--ecos-700", "#366E47");
const svg = useMemo(() => {
return `<svg class="nofill" data-repeated="true" xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 3022 1920' ><g><path fill='${forestBackgroundColor}' fill-rule='evenodd' d='M0 0v1342l8 5c1 2 10 6 20 9 15 5 20 13 41 69 14 35 29 64 31 68 32 60-76 427-76 427h26c27-81 52-191 73-285 19-82 35-150 45-174 15-27 75-97 81-93 3 2 12-4 21-13 8-9 20-17 27-18 6-1 17-8 23-15 9-11 11-12 11-3 0 6 5 10 12 10a539 539 0 0 1 121 257c18 107 21 121 28 118 8-2 8-56 0-201-6-100-6-100 8-125 13-23 28-32 32-20 1 3 10 9 20 13 23 11 25 25 30 169 4 101 60 258 60 258h20s-53-175-58-241c-7-88-2-182 7-198 7-15 12-18 22-14 7 2 18 8 23 13 10 8 11 8 11-1 0-8 11-16 33-24 22 8 33 16 33 24 0 9 1 9 11 1 5-5 16-11 23-13 10-4 15-1 22 14 9 16 9 28 3 111-8 96 17 410 45 410 10 10-26-151-19-340 5-144 7-158 30-169 10-4 19-10 20-13 4-12 19-3 32 20 14 25 14 25 8 125-8 145 20 385 28 388 22 3-18-198 0-305 12-71 34-129 77-201 23-40 36-56 44-56 7 0 12-4 12-10 0-9 2-8 11 3 6 7 17 14 23 15 7 1 19 9 27 18 9 9 18 15 21 13 6-4 66 66 81 93 10 24 26 92 45 174 21 94 46 204 73 285h26s-108-367-76-427c2-4 18-33 31-68 21-56 26-64 41-69 10-3 19-7 20-9l15-9c7-4 12-13 12-22 1-19 8-27 14-17 3 5 8 7 13 4 12-8 35-9 35-1 0 4 2 4 7-1 9-9 16-9 16 1 0 5 4 8 8 8 27-1 45 3 44 9-1 3 9 9 22 12 42 11 66 46 94 137 26 87 169 427 169 427l35 4s-119-279-158-395c-29-88-32-103-30-137 2-39 15-58 33-51 7 3 8-1 6-15-3-18 0-19 19-4 14 12 35 17 35 8 0-5 5-11 11-15 9-6 13-5 19 2 4 5 12 8 18 6 5-1 11 0 12 3s15 9 31 13c29 7 30 8 31 31 18 241 217 558 217 558h20s-224-376-201-573c8-13 21-11 16 3-2 5 1 8 6 8 4 0 8-2 8-5 0-7 29-19 36-15 3 2 8-2 12-9 6-8 12-11 21-8 9 2 14 1 14-5 0-5 1-6 7 0 13 13 22 7 26-16 2-16 6-22 11-20 4 1 14-1 21-5 8-4 16-7 19-6 2 0 6-3 8-8 1-5 7-9 11-9h3c4 8 10 9 26 8 12-1 18 0 14 3-10 7 1 17 22 20 8 1 15 6 17 13 1 6 7 17 14 24 10 12 14 13 21 7 6-5 10-5 15 2 5 8 17 15 33 20 23 8 35 17 35 25 0 9 1 9 11 1 5-5 16-11 23-13 10-4 15-1 22 14 9 16 21 175 21 307 0 127-30 169-11 169 49 0 13-421 49-464 6-7 19-10 20-13 4-12 19-3 32 20 14 25 11 76 11 131 0 70-11 277 23 394h19s-33-224-14-338c12-72 31-108 74-180 23-40 36-56 44-56 7 0 12-4 12-10 0-9 2-8 11 3 6 7 17 14 23 15 7 1 19 9 27 18 9 9 18 15 21 13 6-4 66 66 81 93 10 24 26 92 45 174 21 94 46 204 73 285h26s-108-367-76-427c2-4 18-33 31-68 21-56 26-64 41-69 10-3 19-7 20-9l8-5V0H0Zm397 1334c-5 2-3 7 6 20 9 14 13 16 17 10 8-12 6-17-3-13-5 2-9-1-10-8s-6-11-10-9Zm58 18c0 4-4 5-8 3-5-1-11 4-15 15-7 15-5 21 13 57l21 41v-57c0-32-2-59-5-61-4-2-6-1-6 2Zm-368 15c-12 7-12 7 4 51l16 44v-48c0-27-1-50-4-51-2-1-10 1-16 4Zm916-33c5 2 3 7-6 20-9 14-13 16-17 10-8-12-6-17 3-13 5 2 9-1 10-8s6-11 10-9Zm-58 18c0 4 4 5 8 3 5-1 11 4 15 15 7 15 5 21-13 57l-21 41s3-47 0-69c-4-29 2-47 5-49 4-2 6-1 6 2Zm368 15c12 7 12 7-4 51l-16 44v-48c0-27 1-50 4-51 2-1 10 1 16 4Zm1306-13c9-13 11-18 6-20-4-2-9 2-10 9s-5 10-10 8c-9-4-11 1-3 13 4 6 8 4 17-10Zm-44 1c-4 2-8 1-8-3 0-3-2-4-6-2-3 2-5 29-5 61v57l21-41c18-36 20-42 13-57-4-11-10-16-15-15Zm356 63c16-44 16-44 4-51-6-3-14-5-16-4-3 1-4 24-4 51v48l16-44Z' clip-rule='evenodd'></path></g></svg>`;
}, [forestBackgroundColor]);
return <RepeatBackground aspectRatio={"3022/1920"} svg={svg} />;
};

Some files were not shown because too many files have changed in this diff Show More