chore(ui): setup base for Typography V2

Also removes Button stories for V1 component.
This commit is contained in:
Brian McGee
2025-05-29 15:51:54 +01:00
parent 18d451bfa5
commit ed4581e4ea
18 changed files with 549 additions and 150 deletions

View File

@@ -1,24 +1,55 @@
{ fetchurl, runCommand }:
let
archivo = {
# 400 -> Regular
archivoRegular = fetchurl {
regular = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/Archivo-Regular.woff2";
hash = "sha256-sxWvVJvHnZgKvgvKCmSOz6AQa+G/IFv57YCeY4HyTQo=";
};
# 500 -> Medium
medium = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/Archivo-Medium.woff2";
hash = "sha256-FalQLFry4jdHwuMr1zmxyG7UuI1rn1pd2cV8tmJetRs=";
};
# 600 -> SemiBold
semiBold = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/Archivo-SemiBold.woff2";
hash = "sha256-XrTdFFUNLIdxwgqJB/sX8Om89XqgF/vCz1cYl7I6QTU=";
};
};
archivoSemi = {
# 400 -> Regular
regular = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/ArchivoSemiCondensed-Regular.woff2";
hash = "sha256-3PeB6tMpbYxR9JFyQ+yjpM7bAvZIjcJ4eBiHr9iV5p4=";
};
# 500 -> Medium
archivoMedium = fetchurl {
medium = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/ArchivoSemiCondensed-Medium.woff2";
hash = "sha256-IKaY3YhpmjMaIVUpwKRLd6eFiIihBoAP99I/pwmyll8=";
};
# 600 -> SemiBold
archivoSemiBold = fetchurl {
semiBold = fetchurl {
url = "https://github.com/Omnibus-Type/Archivo/raw/b5d63988ce19d044d3e10362de730af00526b672/fonts/webfonts/ArchivoSemiCondensed-SemiBold.woff2";
hash = "sha256-fOE+b+UeTRoj+sDdUWR1pPCZVn0ABy6FEDDmXrOA4LY=";
};
};
in
runCommand "" { } ''
mkdir -p $out
cp ${archivoRegular} $out/ArchivoSemiCondensed-Regular.woff2
cp ${archivoMedium} $out/ArchivoSemiCondensed-Medium.woff2
cp ${archivoSemiBold} $out/ArchivoSemiCondensed-SemiBold.woff2
cp ${archivo.regular} $out/Archivo-Regular.woff2
cp ${archivo.medium} $out/Archivo-Medium.woff2
cp ${archivo.semiBold} $out/Archivo-SemiBold.woff2
cp ${archivoSemi.regular} $out/ArchivoSemiCondensed-Regular.woff2
cp ${archivoSemi.medium} $out/ArchivoSemiCondensed-Medium.woff2
cp ${archivoSemi.semiBold} $out/ArchivoSemiCondensed-SemiBold.woff2
''

View File

@@ -1,5 +1,7 @@
import type { Preview } from "@kachurun/storybook-solid";
import "@/src/components/v2/index.css";
export const preview: Preview = {
tags: ["autodocs"],
parameters: {
@@ -7,7 +9,7 @@ export const preview: Preview = {
backgrounds: {
values: [
{ name: "Dark", value: "#333" },
{ name: "Light", value: "#F7F9F2" },
{ name: "Light", value: "#ffffff" },
],
default: "Light",
},

View File

@@ -29,6 +29,7 @@
"@storybook/addon-links": "^8.6.14",
"@storybook/addon-viewport": "^8.6.14",
"@storybook/builder-vite": "^8.6.14",
"@storybook/codegen": "^8.6.14",
"@storybook/test-runner": "^0.22.0",
"@tailwindcss/typography": "^0.5.13",
"@types/json-schema": "^7.0.15",

View File

@@ -1,43 +0,0 @@
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Button, ButtonProps } from "./Button";
import FlashIcon from "@/icons/flash.svg";
const meta: Meta<ButtonProps> = {
title: "Components/Button",
component: Button,
};
export default meta;
type Story = StoryObj<ButtonProps>;
const children = "click me";
const startIcon = <FlashIcon width={16} height={16} viewBox="0 0 48 48" />;
export const Default: Story = {
args: {
children,
startIcon,
},
};
export const Small: Story = {
args: {
...Default.args,
size: "s",
},
};
export const Light: Story = {
args: {
...Default.args,
variant: "light",
},
};
export const Ghost: Story = {
args: {
...Default.args,
variant: "ghost",
},
};

View File

@@ -1,89 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Components/Button Default smoke-test 1`] = `
<button class="button button--dark button--dark-hover button--dark-focus button--dark-active disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300 button--default">
<span class="button__icon--start">
<svg xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewbox="0 0 48 48"
>
<path fill-rule="evenodd"
d="M24 6.4h3.2v12.8H40v6.4h-3.2v3.2h-3.2V32h-3.2v3.2h-3.2v3.2H24v3.2h-3.2V28.8H8v-6.4h3.2v-3.2h3.2V16h3.2v-3.2h3.2V9.6H24z"
clip-rule="evenodd"
>
</path>
</svg>
</span>
<span class="text-inherit fnt-clr--inverted fnt-label-default fnt-weight-medium button__label">
click me
</span>
</button>
`;
exports[`Components/Button Ghost smoke-test 1`] = `
<button class="button button--ghost-hover button--ghost-focus button--ghost-active button--default">
<span class="button__icon--start">
<svg xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewbox="0 0 48 48"
>
<path fill-rule="evenodd"
d="M24 6.4h3.2v12.8H40v6.4h-3.2v3.2h-3.2V32h-3.2v3.2h-3.2v3.2H24v3.2h-3.2V28.8H8v-6.4h3.2v-3.2h3.2V16h3.2v-3.2h3.2V9.6H24z"
clip-rule="evenodd"
>
</path>
</svg>
</span>
<span class="text-inherit fnt-label-default fnt-weight-medium button__label">
click me
</span>
</button>
`;
exports[`Components/Button Light smoke-test 1`] = `
<button class="button button--light button--light-hover button--light-focus button--light-active button--default">
<span class="button__icon--start">
<svg xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewbox="0 0 48 48"
>
<path fill-rule="evenodd"
d="M24 6.4h3.2v12.8H40v6.4h-3.2v3.2h-3.2V32h-3.2v3.2h-3.2v3.2H24v3.2h-3.2V28.8H8v-6.4h3.2v-3.2h3.2V16h3.2v-3.2h3.2V9.6H24z"
clip-rule="evenodd"
>
</path>
</svg>
</span>
<span class="text-inherit fnt-label-default fnt-weight-medium button__label">
click me
</span>
</button>
`;
exports[`Components/Button Small smoke-test 1`] = `
<button class="button button--dark button--dark-hover button--dark-focus button--dark-active disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300 button button--small">
<span class="button__icon--start">
<svg xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewbox="0 0 48 48"
>
<path fill-rule="evenodd"
d="M24 6.4h3.2v12.8H40v6.4h-3.2v3.2h-3.2V32h-3.2v3.2h-3.2v3.2H24v3.2h-3.2V28.8H8v-6.4h3.2v-3.2h3.2V16h3.2v-3.2h3.2V9.6H24z"
clip-rule="evenodd"
>
</path>
</svg>
</span>
<span class="text-inherit fnt-clr--inverted fnt-label-s fnt-weight-medium button__label">
click me
</span>
</button>
`;

View File

@@ -0,0 +1,7 @@
## Overview
We will be updating existing components and developing new components in line with the latest designs inside this
folder. As they become ready, they can be copied into the root `components` folder, replacing any existing components as
necessary.
This is to avoid merge hell and allow us to rapidly match the latest designs without the burden of integration.

View File

@@ -0,0 +1,104 @@
/* Body */
.typography {
font-family: "Archivo SemiCondensed", sans-serif;
&.font-weight-normal {
font-weight: 400;
}
&.font-weight-medium {
font-weight: 500;
}
&.font-weight-bold {
font-weight: 600;
}
&.font-body {
&.font-size-default {
font-size: 1rem;
line-height: 132%;
letter-spacing: 0.02rem;
}
&.font-size-s {
font-size: 0.875rem;
line-height: 132%;
letter-spacing: 0.0175rem;
}
&.font-size-xs {
font-size: 0.75rem;
line-height: 132%;
letter-spacing: 0.0225rem;
}
&.font-size-xxs {
font-size: 0.6875rem;
line-height: 132%;
letter-spacing: 0.00688rem;
}
}
&.font-label {
&.font-size-default {
font-size: 0.875rem;
line-height: 132%;
letter-spacing: 0.0175rem;
}
&.font-size-s {
font-size: 0.8125rem;
line-height: 132%;
letter-spacing: 0.0175rem;
}
&.font-size-xs {
font-size: 0.75rem;
line-height: 132%;
letter-spacing: 0.0075rem;
}
}
&.font-title {
&.font-size-default {
font-size: 1.125rem;
line-height: 124%;
letter-spacing: 0.03375rem;
}
&.font-size-m {
font-size: 1.25rem;
line-height: 124%;
letter-spacing: 0.0375rem;
}
&.font-size-l {
font-size: 1.375rem;
line-height: 124%;
letter-spacing: 0.04125rem;
}
}
&.font-headline {
&.font-size-default {
font-size: 1.5rem;
line-height: 116%;
letter-spacing: 0.015rem;
}
&.font-size-m {
font-size: 1.75rem;
line-height: 116%;
letter-spacing: 0.0175rem;
}
&.font-size-l {
font-size: 2rem;
line-height: 116%;
letter-spacing: 0.06rem;
}
}
}

View File

@@ -0,0 +1,106 @@
import type { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Title as BlockTitle, Subtitle, Description, Primary, Stories } from '@storybook/blocks';
import {
AllowedSizes,
Hierarchy,
Typography,
TypographyProps,
Weight,
} from "./Typography";
import { Component, For } from "solid-js";
interface TypographyExamplesProps<H extends Hierarchy> {
weights: Weight[]
sizes: AllowedSizes<H>
hierarchy: Hierarchy;
}
const TypographyExamples: Component<TypographyExamplesProps> = (props) => (
<table class="w-full text-left table-auto min-w-max">
<tbody>
<For each={props.sizes}>
{(size) => (
<tr>
<For each={props.weights}>
{(weight) => (
<td class="p-2 border-b">
<Typography
hierarchy={props.hierarchy}
size={size}
weight={weight}
>
{props.hierarchy} / {size} / {weight}
</Typography>
</td>
)}
</For>
</tr>
)}
</For>
</tbody>
</table>
);
const meta: Meta<TypographyExamplesProps<never>> = {
title: "Components/Typography",
component: TypographyExamples,
parameters: {
docs: {
// custom page template to remove controls and primary story example
// gives a much nicer overview
page: () => (
<>
<BlockTitle />
<Subtitle />
<Description />
<Stories />
</>
),
}
},
decorators: [
(Story) => (
<div className="bg-white">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<TypographyProps>;
export const Body: Story = {
args: {
hierarchy: "body",
sizes: ["default", "s", "xs", "xxs"],
weights: ["normal", "medium", "bold"]
},
};
export const Label: Story = {
args: {
hierarchy: "label",
sizes: ["default", "s", "xs"],
weights: ["normal", "medium", "bold"]
},
};
export const Title: Story = {
args: {
hierarchy: "title",
sizes: ["default", "s", "xs"],
weights: ["normal", "medium", "bold"]
},
}
export const Headline: Story = {
args: {
hierarchy: "headline",
sizes: ["default", "s", "xs"],
weights: ["normal", "medium", "bold"]
},
}

View File

@@ -0,0 +1,98 @@
import { type JSX } from "solid-js";
import { Dynamic } from "solid-js/web";
import cx from "classnames";
import "./Typography.css";
export type Tag = "span" | "p" | "h1" | "h2" | "h3" | "h4" | "div";
export type Hierarchy = "body" | "title" | "headline" | "label";
export type Weight = "normal" | "medium" | "bold";
// type Size = "default" | "xs" | "s" | "m" | "l";
interface SizeForHierarchy {
body: {
default: string;
s: string;
xs: string;
xxs: string;
};
label: {
default: string;
s: string;
xs: string;
};
headline: {
default: string;
m: string;
l: string;
};
title: {
default: string;
m: string;
l: string;
};
}
export type AllowedSizes<H extends Hierarchy> = keyof SizeForHierarchy[H];
const sizeHierarchyMap: SizeForHierarchy = {
body: {
default: cx("font-size-default"),
s: cx("font-size-s"),
xs: cx("font-size-xs"),
xxs: cx("font-size-xxs"),
},
headline: {
default: cx("font-size-default"),
m: cx("font-size-m"),
l: cx("font-size-l"),
},
title: {
default: cx("font-size-default"),
// xs: cx("font-size-xs"),
// s: cx("font-size-s"),
m: cx("font-size-m"),
l: cx("font-size-l"),
},
label: {
default: cx("font-size-default"),
s: cx("font-size-s"),
xs: cx("font-size-xs"),
},
};
const weightMap: Record<Weight, string> = {
normal: cx("font-weight-normal"),
medium: cx("font-weight-medium"),
bold: cx("font-weight-bold"),
};
interface _TypographyProps<H extends Hierarchy> {
hierarchy: H;
size: AllowedSizes<H>;
children: JSX.Element;
weight?: Weight;
inverted?: boolean;
tag?: Tag;
class?: string;
classList?: Record<string, boolean>;
}
export const Typography = <H extends Hierarchy>(props: _TypographyProps<H>) => {
return (
<Dynamic
component={props.tag || "span"}
class={cx(
"typography",
weightMap[props.weight || "normal"],
`font-${props.hierarchy}`,
sizeHierarchyMap[props.hierarchy][props.size] as string,
props.class,
)}
classList={props.classList}
>
{props.children}
</Dynamic>
);
};
export type TypographyProps = _TypographyProps<Hierarchy>;

View File

@@ -0,0 +1,23 @@
.fnt-clr-primary {
color: var(--clr-fg-def-1);
}
.fnt-clr-secondary {
color: var(--clr-fg-def-2);
}
.fnt-clr-tertiary {
color: var(--clr-fg-def-3);
}
.fnt-clr-primary.fnt-clr--inverted {
color: var(--clr-fg-inv-1);
}
.fnt-clr-secondary.fnt-clr--inverted {
color: var(--clr-fg-inv-2);
}
.fnt-clr-tertiary.fnt-clr--inverted {
color: var(--clr-fg-inv-3);
}

View File

@@ -0,0 +1,4 @@
@import "./typography-label.css";
@import "./typography-body.css";
@import "./typography-title.css";
@import "./typography-headline.css";

View File

@@ -0,0 +1,23 @@
.fnt-body-default {
font-size: 1rem;
line-height: 132%;
letter-spacing: 3%;
}
.fnt-body-s {
font-size: 0.925rem;
line-height: 132%;
letter-spacing: 3%;
}
.fnt-body-xs {
font-size: 0.875rem;
line-height: 132%;
letter-spacing: 3%;
}
.fnt-body-xxs {
font-size: 0.75rem;
line-height: 132%;
letter-spacing: 0.00688rem;
}

View File

@@ -0,0 +1,17 @@
.fnt-headline-default {
font-size: 1.5rem;
line-height: 116%;
letter-spacing: 1%;
}
.fnt-headline-m {
font-size: 1.75rem;
line-height: 116%;
letter-spacing: 1%;
}
.fnt-headline-l {
font-size: 2rem;
line-height: 116%;
letter-spacing: 1%;
}

View File

@@ -0,0 +1,14 @@
.fnt-label-default {
font-size: 0.8125rem;
line-height: 100%;
}
.fnt-label-s {
font-size: 0.75rem;
line-height: 100%;
}
.fnt-label-xs {
font-size: 0.6875rem;
line-height: 100%;
}

View File

@@ -0,0 +1,17 @@
.fnt-title-default {
font-size: 1.125rem;
line-height: 124%;
letter-spacing: 3%;
}
.fnt-title-m {
font-size: 1.25rem;
line-height: 124%;
letter-spacing: 3%;
}
.fnt-title-l {
font-size: 1.375rem;
line-height: 124%;
letter-spacing: 3%;
}

View File

@@ -0,0 +1,26 @@
@import "./typography-hierarchy/";
@import "./typography-color.css";
.fnt-weight-normal {
font-weight: 300;
}
.fnt-weight-medium {
font-weight: 500;
}
.fnt-weight-bold {
font-weight: 700;
}
.fnt-weight-normal.fnt-clr--inverted {
font-weight: 300;
}
.fnt-weight-medium.fnt-clr--inverted {
font-weight: 400;
}
.fnt-weight-bold.fnt-clr--inverted {
font-weight: 700;
}

View File

@@ -0,0 +1,58 @@
@import "material-icons/iconfont/filled.css";
/* List of icons: https://marella.me/material-icons/demo/ */
/* @import url(./components/Typography/css/typography.css); */
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: "Archivo";
font-weight: 400;
src: url(../../../.fonts/Archivo-Regular.woff2) format("woff2");
}
@font-face {
font-family: "Archivo";
font-weight: 500;
src: url(../../../.fonts/Archivo-Medium.woff2) format("woff2");
}
@font-face {
font-family: "Archivo";
font-weight: 600;
src: url(../../../.fonts/Archivo-SemiBold.woff2) format("woff2");
}
@font-face {
font-family: "Archivo SemiCondensed";
font-weight: 400;
src: url(../../../.fonts/ArchivoSemiCondensed-Regular.woff2) format("woff2");
}
@font-face {
font-family: "Archivo SemiCondensed";
font-weight: 500;
src: url(../../../.fonts/ArchivoSemiCondensed-Medium.woff2) format("woff2");
}
@font-face {
font-family: "Archivo SemiCondensed";
font-weight: 600;
src: url(../../../.fonts/ArchivoSemiCondensed-SemiBold.woff2) format("woff2");
}
html {
@apply font-sans;
overflow-x: hidden;
overflow-y: hidden;
-webkit-user-select: none;
/* Safari */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* Internet Explorer/Edge */
user-select: none;
/* Standard */
}

View File

@@ -3,7 +3,7 @@ import type { Config } from "tailwindcss";
export const typography: Partial<Config["theme"]> = {
fontFamily: {
sans: ["Archivo", ...defaultTheme.fontFamily.sans],
sans: ["Archivo SemiCondensed", ...defaultTheme.fontFamily.sans],
},
fontSize: {
...defaultTheme.fontSize,