From b9d252f88002fc87fa007ac4f279ef7dbd960266 Mon Sep 17 00:00:00 2001 From: Zohar Manor-Abel Date: Wed, 17 Jun 2026 16:16:36 +0100 Subject: [PATCH 1/2] Added typography fonts and styles and into theme through tokens Update pnpm lock and package Dependency and call update - Adding calls directly in theme - Switched storybook stylesheet calls - Fixed typography calls and updated imports - updated tests --- .storybook/preview.tsx | 3 + .storybook/storybook.css | 27 +-- package.json | 10 +- pnpm-lock.yaml | 50 +++--- src/styles/diamondDS/DiamondDSTypography.css | 70 ++++++++ src/themes/DiamondDSTheme.test.ts | 22 ++- src/themes/DiamondDSTheme.ts | 78 ++++++-- src/themes/createDiamondTypography.ts | 176 +++++++++++++++++++ 8 files changed, 367 insertions(+), 69 deletions(-) create mode 100644 src/styles/diamondDS/DiamondDSTypography.css create mode 100644 src/themes/createDiamondTypography.ts diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 06b272ff..e2993c97 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -2,6 +2,9 @@ import React from "react"; import { CssBaseline } from "@mui/material"; import type { Preview } from "@storybook/react"; import "@fontsource-variable/inter"; +import "@fontsource-variable/outfit"; +import "@fontsource/ibm-plex-mono"; +import "@fontsource/ibm-plex-mono/600.css"; import "./storybook.css"; /* Storybook CSS override */ import { ThemeProvider } from "../src"; import { DiamondDSTheme } from "../src"; diff --git a/.storybook/storybook.css b/.storybook/storybook.css index 3c2f3c54..74e4a964 100644 --- a/.storybook/storybook.css +++ b/.storybook/storybook.css @@ -1,16 +1,4 @@ -:root:not(.MuiTypography-root) { - --sb-ds-font-body: - "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Helvetica, Arial, sans-serif; - - --sb-ds-font-heading: - "Outfit", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Helvetica, Arial, sans-serif; - - --sb-ds-font-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; - +:root { --sb-ds-background: #f6f6f9; --sb-ds-surface: #ffffff; --sb-ds-surface-container: #eef1f5; @@ -43,14 +31,14 @@ body .sbdocs:not(.MuiTypography-root) { .sbdocs li:not(.MuiTypography-root), .sbdocs :where(div:not(.sb-anchor, .sb-unstyled, .sb-unstyled div)), .sbdocs :where(span:not(.sb-anchor, .sb-unstyled, .sb-unstyled span)) { - font-family: var(--sb-ds-font-body) !important; + font-family: var(--ds-font-family-default) !important; } /* Headings use Outfit or Inter */ .sbdocs h1.sbdocs-title, .sbdocs.sbdocs-content h1:not(.MuiTypography-root) { - font-family: var(--sb-ds-font-heading); + font-family: var(--ds-font-family-display); } .sbdocs h2:not(.MuiTypography-root), @@ -60,7 +48,7 @@ body .sbdocs:not(.MuiTypography-root) { .sbdocs h6:not(.MuiTypography-root), table th:not(.MuiTypography-root), table td:not(.MuiTypography-root) { - font-family: var(--sb-ds-font-body); + font-family: var(--ds-font-family-default); } /* Code uses IBM Plex Mono */ code:not(.MuiTypography-root), @@ -70,8 +58,9 @@ code:not(.MuiTypography-root), .sbdocs samp:not(.MuiTypography-root), .token:not(.MuiTypography-root), pre .language-text:not(.MuiTypography-root), -pre .language-tsx:not(.MuiTypography-root) { - font-family: var(--sb-ds-font-mono) !important; +pre .language-tsx:not(.MuiTypography-root), +pre .language-shell:not(.MuiTypography-root) { + font-family: var(--ds-font-family-mono) !important; } /* DS DOCS */ @@ -170,7 +159,7 @@ pre .language-tsx:not(.MuiTypography-root) { } .ds-docs .ds-table th, .ds-docs .ds-table td { - font-family: var(--sb-ds-font-body); + font-family: var(--ds-font-family-default); border-bottom: 1px solid var(--sb-ds-border-subtle); padding: 10px 12px; text-align: left; diff --git a/package.json b/package.json index 3fcd948b..0cc20ef0 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,9 @@ "dev:preview": "pnpm --dir ./dev preview" }, "dependencies": { - "@fontsource-variable/inter": "^5.2.8", + "@fontsource-variable/inter": "^5.3.0", + "@fontsource-variable/outfit": "^5.3.0", + "@fontsource/ibm-plex-mono": "^5.3.0", "@mui/icons-material": "^7.0.0", "react-icons": "^5.3.0", "utif": "^3.1.0" @@ -81,9 +83,9 @@ "@emotion/styled": "^11.13.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^10.0.1", - "@fontsource/ibm-plex-mono": "^5.2.7", - "@fontsource/inter": "^5.2.8", - "@fontsource/outfit": "^5.2.8", + "@fontsource/ibm-plex-mono": "^5.3.0", + "@fontsource/inter": "^5.3.0", + "@fontsource/outfit": "^5.3.0", "@jsonforms/core": "^3.7.0", "@jsonforms/material-renderers": "^3.7.0", "@jsonforms/react": "^3.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e43b0cc..3cc5db37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,14 @@ importers: .: dependencies: '@fontsource-variable/inter': - specifier: ^5.2.8 - version: 5.2.8 + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource-variable/outfit': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/ibm-plex-mono': + specifier: ^5.3.0 + version: 5.3.0 '@mui/icons-material': specifier: ^7.0.0 version: 7.3.11(@mui/material@7.3.11(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.3.1))(@types/react@18.3.31)(react@18.3.1))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.31)(react@18.3.1) @@ -69,15 +75,12 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@9.39.4) - '@fontsource/ibm-plex-mono': - specifier: ^5.2.7 - version: 5.2.7 '@fontsource/inter': - specifier: ^5.2.8 - version: 5.2.8 + specifier: ^5.3.0 + version: 5.3.0 '@fontsource/outfit': - specifier: ^5.2.8 - version: 5.2.8 + specifier: ^5.3.0 + version: 5.3.0 '@jsonforms/core': specifier: ^3.7.0 version: 3.7.0 @@ -1143,17 +1146,20 @@ packages: '@noble/hashes': optional: true - '@fontsource-variable/inter@5.2.8': - resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} + '@fontsource-variable/inter@5.3.0': + resolution: {integrity: sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==} - '@fontsource/ibm-plex-mono@5.2.7': - resolution: {integrity: sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==} + '@fontsource-variable/outfit@5.3.0': + resolution: {integrity: sha512-fPb5XnYK0k7SbR++HEmm4+ZqDszmLKQ+sPRUr8oQ/KNC0vy6r6hazJFe8iYozv4cqlHbvJ/JECkJWrfDodUMVQ==} - '@fontsource/inter@5.2.8': - resolution: {integrity: sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==} + '@fontsource/ibm-plex-mono@5.3.0': + resolution: {integrity: sha512-eTgnZjZEGk1QtD3ZstF+Vclo2HLAni8YMy34/DxllwZvyz1lR/1RF/xTiAquOBO7MvqBx8D2Ig2WCPMVfdZu7Q==} - '@fontsource/outfit@5.2.8': - resolution: {integrity: sha512-rXC6g0MqD7cOBjht0bMqc43qM6lRqDLML9KXsmg9uykz0wLQhy8Z/ajrMG6iyoT3NJR+MYgU+OEHp7uHoTb+Yw==} + '@fontsource/inter@5.3.0': + resolution: {integrity: sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==} + + '@fontsource/outfit@5.3.0': + resolution: {integrity: sha512-0AVHzTTVJSxWDOxPKSfMeHhZeBFDU3cmgrcOb76ZEyhGo0+xgyG3GsLF3h9+X8w0IX7G4jzjJgFQEBiWdX6VgA==} '@gerrit0/mini-shiki@3.23.0': resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} @@ -6097,13 +6103,15 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@fontsource-variable/inter@5.2.8': {} + '@fontsource-variable/inter@5.3.0': {} + + '@fontsource-variable/outfit@5.3.0': {} - '@fontsource/ibm-plex-mono@5.2.7': {} + '@fontsource/ibm-plex-mono@5.3.0': {} - '@fontsource/inter@5.2.8': {} + '@fontsource/inter@5.3.0': {} - '@fontsource/outfit@5.2.8': {} + '@fontsource/outfit@5.3.0': {} '@gerrit0/mini-shiki@3.23.0': dependencies: diff --git a/src/styles/diamondDS/DiamondDSTypography.css b/src/styles/diamondDS/DiamondDSTypography.css new file mode 100644 index 00000000..a45902cf --- /dev/null +++ b/src/styles/diamondDS/DiamondDSTypography.css @@ -0,0 +1,70 @@ +:root { + --ds-font-family-default: "Inter Variable", system-ui, sans-serif; + --ds-font-family-display: + "Outfit Variable", "Inter Variable", system-ui, sans-serif; + --ds-font-family-mono: "IBM Plex Mono", ui-monospace, monospace; + + /* TYPOGRAPHY SCALE */ + --ds-font-size-100: 0.6875rem; /* 11px */ + --ds-font-size-200: 0.75rem; /* 12px */ + --ds-font-size-300: 0.875rem; /* 14px */ + --ds-font-size-400: 1rem; /* 16px */ + --ds-font-size-500: 1.125rem; /* 18px */ + --ds-font-size-600: 1.25rem; /* 20px */ + --ds-font-size-700: 1.75rem; /* 28px */ + --ds-font-size-800: 2rem; /* 32px */ + --ds-font-size-900: 2.125rem; /* 34px */ + --ds-font-size-1000: 3rem; /* 48px */ + --ds-font-size-1100: 4.5rem; /* 72px */ + + /* TYPE SIZES */ + --ds-type-h1-size: var(--ds-font-size-1000); + --ds-type-h2-size: var(--ds-font-size-900); + --ds-type-h3-size: var(--ds-font-size-800); + --ds-type-h4-size: var(--ds-font-size-700); + --ds-type-h5-size: var(--ds-font-size-600); + --ds-type-h6-size: var(--ds-font-size-500); + + --ds-type-display-h1-size: var(--ds-font-size-1100); + + --ds-type-display-h2-size: var(--ds-font-size-1000); + --ds-type-display-h3-size: var(--ds-font-size-900); + --ds-type-display-h4-size: var(--ds-font-size-700); + + --ds-type-lead-size: var(--ds-font-size-600); + + --ds-type-body1-size: var(--ds-font-size-400); + --ds-type-body2-size: var(--ds-font-size-300); + + --ds-type-subtitle1-size: var(--ds-font-size-400); + --ds-type-subtitle2-size: var(--ds-font-size-300); + + --ds-type-overline-size: var(--ds-font-size-200); + --ds-type-overline-small-size: var(--ds-font-size-100); + + --ds-type-caption-size: var(--ds-font-size-200); + --ds-type-meta-size: var(--ds-font-size-200); + + --ds-type-mono1-size: var(--ds-font-size-400); + --ds-type-mono2-size: var(--ds-font-size-300); + --ds-type-mono3-size: var(--ds-font-size-200); + + /* WEIGHT SIZES */ + --ds-font-weight-light: 300; + --ds-font-weight-regular: 400; + --ds-font-weight-medium: 500; + --ds-font-weight-semibold: 600; + --ds-font-weight-bold: 700; + + /* LINE HEIGHTS */ + --ds-line-height-tight: 1.1; + --ds-line-height-condensed: 1.2; + --ds-line-height-normal: 1.4; + --ds-line-height-relaxed: 1.5; + + /* LETTER SPACING */ + --ds-letter-spacing-tight: -0.03em; + --ds-letter-spacing-condensed: -0.01em; + --ds-letter-spacing-normal: 0; + --ds-letter-spacing-wide: 0.08em; +} diff --git a/src/themes/DiamondDSTheme.test.ts b/src/themes/DiamondDSTheme.test.ts index 3f61072c..cb426663 100644 --- a/src/themes/DiamondDSTheme.test.ts +++ b/src/themes/DiamondDSTheme.test.ts @@ -13,6 +13,14 @@ const tokensCss = readFileSync( "utf8", ); +const typographyCss = readFileSync( + resolve( + dirname(fileURLToPath(import.meta.url)), + "../styles/diamondDS/DiamondDSTypography.css", + ), + "utf8", +); + const getVarNames = (value: unknown): string[] => { if (typeof value !== "string") return []; return [...value.matchAll(/var\((--[^),\s]+)/g)].map((match) => match[1]); @@ -133,7 +141,7 @@ const getReferencedDiamondVars = (): Set => const getDefinedDiamondVars = (): Set => new Set( - [...tokensCss.matchAll(/(--ds-[a-zA-Z0-9-]+)\s*:/g)].map( + [...(tokensCss + typographyCss).matchAll(/(--ds-[a-zA-Z0-9-]+)\s*:/g)].map( (match) => match[1], ), ); @@ -186,17 +194,7 @@ describe("DiamondDSTheme", () => { it("configures the DiamondDS font stack", () => { expect(DiamondDSTheme.typography.fontFamily).toBe( - [ - '"Inter Variable"', - "Inter", - "system-ui", - "-apple-system", - '"Segoe UI"', - "Roboto", - "Helvetica", - "Arial", - "sans-serif", - ].join(","), + "var(--ds-font-family-default)", ); }); diff --git a/src/themes/DiamondDSTheme.ts b/src/themes/DiamondDSTheme.ts index 38daf1a6..a29474af 100644 --- a/src/themes/DiamondDSTheme.ts +++ b/src/themes/DiamondDSTheme.ts @@ -15,13 +15,20 @@ * Components should consume semantic roles from the theme or semantic CSS * variables rather than raw colour values. */ +import "@fontsource-variable/inter"; +import "@fontsource-variable/outfit"; +import "@fontsource/ibm-plex-mono"; +import "@fontsource/ibm-plex-mono/600.css"; import "../styles/diamondDS/DiamondDSTokens.css"; +import "../styles/diamondDS/DiamondDSTypography.css"; // Enables `theme.vars` typings for MUI CSS variable themes. import type {} from "@mui/material/themeCssVarsAugmentation"; import { extendTheme } from "@mui/material/styles"; import type { CSSObject, PaletteColor, Theme } from "@mui/material/styles"; +import { createDiamondTypography } from "./createDiamondTypography"; + /** * Component prop types are used to type `ownerState` inside MUI style overrides. */ @@ -120,6 +127,30 @@ type IntentPaletteRecord = Record; * text, surface, border and palette roles through the MUI theme API. */ declare module "@mui/material/styles" { + interface TypographyVariants { + h1Display: React.CSSProperties; + h2Display: React.CSSProperties; + h3Display: React.CSSProperties; + h4Display: React.CSSProperties; + lead: React.CSSProperties; + overlineSmall: React.CSSProperties; + mono1: React.CSSProperties; + mono2: React.CSSProperties; + mono3: React.CSSProperties; + } + + interface TypographyVariantsOptions { + h1Display?: React.CSSProperties; + h2Display?: React.CSSProperties; + h3Display?: React.CSSProperties; + h4Display?: React.CSSProperties; + lead?: React.CSSProperties; + overlineSmal?: React.CSSProperties; + mono1?: React.CSSProperties; + mono2?: React.CSSProperties; + mono3?: React.CSSProperties; + } + interface CssVarsTheme { logos?: { normal: ImageColourSchemeSwitchType; @@ -250,6 +281,21 @@ declare module "@mui/material/styles" { } } +/* Typography for branded headers */ +declare module "@mui/material/Typography" { + interface TypographyPropsVariantOverrides { + h1Display: true; + h2Display: true; + h3Display: true; + h4Display: true; + lead: true; + overlineSmall: true; + mono1: true; + mono2: true; + mono3: true; + } +} + export type DSMode = "light" | "dark"; type PaletteAdapter = { @@ -669,19 +715,7 @@ const DiamondDSTheme = extendTheme({ }, }, - typography: { - fontFamily: [ - '"Inter Variable"', - "Inter", - "system-ui", - "-apple-system", - '"Segoe UI"', - "Roboto", - "Helvetica", - "Arial", - "sans-serif", - ].join(","), - }, + typography: createDiamondTypography(), logos: { normal: { @@ -741,6 +775,9 @@ const DiamondDSTheme = extendTheme({ * Feedback surfaces: * MuiSnackbar → layout constraints * MuiSnackbarContent → surface styling and actions + * + * Font Style overrides: + * MuiCssBaseline → typography */ MuiButtonBase: { @@ -1651,6 +1688,21 @@ const DiamondDSTheme = extendTheme({ }, }, }, + + MuiCssBaseline: { + styleOverrides: { + "code, kbd, samp, pre": { + fontFamily: "var(--ds-font-family-mono)", + fontSize: "0.875em", + lineHeight: "var(--ds-line-height-normal)", + }, + + pre: { + fontSize: "var(--ds-font-size-300)", + overflowX: "auto", + }, + }, + }, }, }); diff --git a/src/themes/createDiamondTypography.ts b/src/themes/createDiamondTypography.ts new file mode 100644 index 00000000..c05001d8 --- /dev/null +++ b/src/themes/createDiamondTypography.ts @@ -0,0 +1,176 @@ +export const createDiamondTypography = () => ({ + fontFamily: "var(--ds-font-family-default)", + + h1Display: { + fontFamily: "var(--ds-font-family-display)", + fontSize: "var(--ds-type-display-h1-size)", + fontWeight: "var(--ds-font-weight-medium)", + lineHeight: "var(--ds-line-height-tight)", + letterSpacing: "var(--ds-letter-spacing-tight)", + }, + + h2Display: { + fontFamily: "var(--ds-font-family-display)", + fontSize: "var(--ds-type-display-h2-size)", + fontWeight: "var(--ds-font-weight-medium)", + lineHeight: "var(--ds-line-height-tight)", + letterSpacing: "var(--ds-letter-spacing-tight)", + }, + + h3Display: { + fontFamily: "var(--ds-font-family-display)", + fontSize: "var(--ds-type-display-h3-size)", + fontWeight: "var(--ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h4Display: { + fontFamily: "var(--ds-font-family-display)", + fontSize: "var(--ds-type-display-h4-size)", + fontWeight: "var(--ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h1: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h1-size)", + fontWeight: "var( --ds-font-weight-medium)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h2: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h2-size)", + fontWeight: "var( --ds-font-weight-medium)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h3: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h3-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h4: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h4-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-condensed)", + }, + + h5: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h5-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + h6: { + fontFamily: "var(--ds-font-family-default)", + fontSize: "var(--ds-type-h6-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + lead: { + fontSize: "var(--ds-type-lead-size)", + fontWeight: "var( --ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + body1: { + fontSize: "var(--ds-type-body1-size)", + fontWeight: "var( --ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + body2: { + fontSize: "var(--ds-type-body2-size)", + fontWeight: "var( --ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + subtitle1: { + fontSize: "var(--ds-type-subtitle1-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + subtitle2: { + fontSize: "var(--ds-type-subtitle2-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + overline: { + fontSize: "var(--ds-type-overline-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-wide)", + textTransform: "uppercase", + }, + + overlineSmall: { + fontSize: "var(--ds-type-overline-small-size)", + fontWeight: "var( --ds-font-weight-semibold)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-wide)", + textTransform: "uppercase", + }, + + caption: { + fontSize: "var(--ds-type-caption-size)", + fontWeight: "var( --ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-normal)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + meta: { + fontSize: "var(--ds-type-caption-size)", + fontWeight: "var( --ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + mono1: { + fontFamily: "var(--ds-font-family-mono)", + fontSize: "var(--ds-type-mono1-size)", + fontWeight: "var(--ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-relaxed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + mono2: { + fontFamily: "var(--ds-font-family-mono)", + fontSize: "var(--ds-type-mono2-size)", + fontWeight: "var(--ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-normal)", + }, + + mono3: { + fontFamily: "var(--ds-font-family-mono)", + fontSize: "var(--ds-type-mono3-size)", + fontWeight: "var(--ds-font-weight-regular)", + lineHeight: "var(--ds-line-height-condensed)", + letterSpacing: "var(--ds-letter-spacing-normal)", + }, + + button: { + fontWeight: "var( --ds-font-weight-semibold)", + textTransform: "none", + }, +}); From 54e3662536bceab901a5bd8ad0d8b7e53a0a555c Mon Sep 17 00:00:00 2001 From: Zohar Manor-Abel Date: Tue, 16 Jun 2026 16:22:53 +0100 Subject: [PATCH 2/2] Adding Typography and Icons docs to Foundation Added: 2. typography 3. Icons + - Added icons pack to show icons - Updated typography to align with #222 - Icons is standalone --- package.json | 1 + pnpm-lock.yaml | 12 + src/storybook/foundation/2. typography.mdx | 518 +++++++++++++++++++++ src/storybook/foundation/3. icons.mdx | 466 ++++++++++++++++++ 4 files changed, 997 insertions(+) create mode 100644 src/storybook/foundation/2. typography.mdx create mode 100644 src/storybook/foundation/3. icons.mdx diff --git a/package.json b/package.json index 0cc20ef0..9b56594f 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,7 @@ "gh-pages": "^6.2.0", "jsdom": "^29.1.1", "keycloak-js": "^26.2.1", + "lucide-react": "^1.26.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.12.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3cc5db37..eb1ae02a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,6 +195,9 @@ importers: jsdom: specifier: ^29.1.1 version: 29.1.1 + lucide-react: + specifier: ^1.26.0 + version: 1.26.0(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -3678,6 +3681,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@1.26.0: + resolution: {integrity: sha512-raglYVR2+VkMfJL158krjVmE+rV5ST2lzA/KQm1FRSjMHT4MnWaegHxoVEpmc2So3nOEhp9oGejJwAPX8MoAjg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -8853,6 +8861,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@1.26.0(react@18.3.1): + dependencies: + react: 18.3.1 + lunr@2.3.9: {} lz-string@1.5.0: {} diff --git a/src/storybook/foundation/2. typography.mdx b/src/storybook/foundation/2. typography.mdx new file mode 100644 index 00000000..92aaf486 --- /dev/null +++ b/src/storybook/foundation/2. typography.mdx @@ -0,0 +1,518 @@ +import { Meta } from "@storybook/blocks"; + + + +
+ +# Typography + + + Typography in Diamond DS should support readability, scanning, and technical + clarity. Interfaces are often used in dense scientific workflows, so type + should feel calm, consistent, and easy to parse. + + +## Typefaces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypefaceTokenMUI rolesPurposeTypical usage
Inter + --ds-font-family-default + + body1, body2, subtitle1, + subtitle2, caption,meta,{" "} + button + Default interface typefaceUI text, controls, tables, navigation, forms
Outfit + --ds-font-family-display + + h1Display, h2Display,h3Display,{" "} + h4Display + Display and high-level headingsProduct headings, landing areas, documentation hero areas
IBM Plex Mono + --ds-font-family-mono + + mono1, mono2,mono3 + Technical and aligned contentIDs, timestamps, code, logs, numeric values, instrument output
+ +## Core principle + +Use typography to make information easier to understand, not more decorative. + +Scientific interfaces often contain dense data, status information, technical +labels, long identifiers, and changing values. Typography should help users see +what matters, compare values, and act with confidence. + +## Hierarchy + +Use a clear and repeatable hierarchy across products. + +- Use headings to structure the page. +- Use body text for explanation and supporting content. +- Use meta text for compact operational information and status. +- Use labels for controls, fields, table headers, and short descriptors. +- Use mono type for technical values where alignment or precision matters. + +Avoid creating one-off heading sizes or local typographic styles unless there is +a reusable reason. + +Use heading levels in order where possible. Avoid jumping directly from h1 to h4 unless the surrounding structure justifies it. + +## Typography variants + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GroupVariantsUse for
Display + h1Display, h2Display, h3Display,{" "} + h4Display + Public-facing pages, landing areas, documentation hero sections
Headings + h1h6 + Application and document structure
Body + lead, body1, body2,{" "} + subtitle1, subtitle2 + Readable interface text and supporting content
Utility + caption, meta, overline,{" "} + overlineSmall + + Supporting text, operational metadata, grouping labels, status bars and + compact section labels +
Mono + mono1, mono2, mono3 + Logs, console output, IDs, paths, timestamps, technical values
+ +

+ Subtitle variants are intended for supporting headings and structured content. + Use body variants for prose and explanatory text. +

+ +## Typography scale + +Diamond DS provides a shared typography scale that underpins all typography +variants. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenSize
+ --ds-font-size-100 + 0.6875rem (11px)
+ --ds-font-size-200 + 0.75rem (12px)
+ --ds-font-size-300 + 0.875rem (14px)
+ --ds-font-size-400 + 1rem (16px)
+ --ds-font-size-500 + 1.125rem (18px)
+ --ds-font-size-600 + 1.25rem (20px)
+ --ds-font-size-700 + 1.75rem (28px)
+ --ds-font-size-800 + 2rem (32px)
+ --ds-font-size-900 + 2.125rem (34px)
+ --ds-font-size-1000 + 3rem (48px)
+ --ds-font-size-1100 + 4.5rem (72px)
+ +Typography variants should normally be used directly. + +Font size tokens are primarily intended for creating reusable components, +defining typography variants, and establishing consistent scale across the +design system. + +Avoid applying arbitrary font sizes when an existing typography variant already +communicates the correct hierarchy and meaning. + +## Display and heading variants + +Use display variants for large, high-level moments such as landing pages, +welcome screens, product introductions, and documentation hero areas. + +Use heading variants for application structure and content hierarchy. + +Display variants should be used sparingly and are not intended to replace the +standard heading hierarchy within operational interfaces. + +
+

+ The current typography model uses dedicated display variants ( + h1Displayh4Display) for display typography and + h1h6 for application hierarchy. +

+ +

+ As the design system evolves, h1 and/or h2 may + adopt the display typeface in some contexts. This decision is still under + review and may change based on application and public-facing usage patterns. +

+
+ +## Choosing a typeface + +

Inter

+ +Inter is the default interface typeface. + +Use Inter for: + +- Navigation +- Buttons +- Forms +- Tables +- Dialogues +- Cards +- Body text +- Labels +- Status messages + +Inter should be the default choice unless there is a specific reason to use +Outfit or IBM Plex Mono. + +

Outfit

+ +Outfit is used for display and high-level headings. + +Use Outfit sparingly for: + +- Product names +- Major page titles +- Welcome or landing areas +- Documentation hero headings + +Do not use Outfit for dense application UI, tables, forms, or technical values. + +

IBM Plex Mono

+ +IBM Plex Mono is used for technical and aligned information. + +Use IBM Plex Mono for: + +- Beamline IDs +- Sample IDs +- Proposal IDs +- Timestamps +- Code +- Logs +- Console output +- Numeric columns +- Technical table values +- Numeric values where alignment helps comparison +- File paths +- API-like values + +Do not use mono type for general UI text. It reduces readability when used too +widely. + +## Technical values + +Technical values should be easy to scan, compare, and copy. + +Use mono type where users need to inspect precise values: + +```tsx + + energy=12.4 keV · exposure=0.5 s · frames=100 + +``` + +For tables with mixed content, use mono only for the technical cells rather than +the whole table. + +## Numbers and units + +Keep numbers and units readable. + +```text +12.4 keV +0.5 s +180 ms +1,024 frames +``` + +Prefer a space between number and unit unless the notation convention says +otherwise. + +Avoid inconsistent formatting: + +```text +12.4keV +0.5sec +180 milliseconds +1024 Frames +``` + +## Overlines and grouping + +Overlines provide lightweight hierarchy and grouping without introducing a full +heading. + +They are particularly useful in dense scientific interfaces where additional +structure is needed without increasing visual weight. + +Use overlines to: + +- Group related controls +- Separate sections within cards or panels +- Label groups of settings +- Introduce collections of information +- Provide context above headings or values + +```tsx + + Acquisition + + + + Detector settings + +``` + +Prefer overlines when a full heading would create unnecessary visual weight. + +## Utility text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariantPurposeTypical usage
+ caption + Small explanatory or supporting textHelper text, figure captions, secondary descriptions
+ meta + Small operational informationStatus bars, timestamps, counts, short technical metadata
+ overline + Grouping and categorisationSection labels, grouped controls, settings groups, card sections
+ overlineSmall + Compact grouping and dense labellingDense panels, compact cards, highly constrained layouts
+ +### Meta + +Use meta for compact operational information such as: + +- Status bars +- Timestamps +- Counts +- Short technical metadata +- Connection status +- Acquisition state + +## Logs and console output + +Logs and console output should use mono type. + +```tsx + + [14:30:15] Starting data acquisition pipeline [14:30:18] Detector initialised + successfully [14:30:22] Scan started — energy 12.4 keV, exposure 0.5 s + +``` + +Use colour and status tokens carefully. Typography should not be the only way to +communicate severity. + +## Do and don't + +
+
+

✅ Do

+
    +
  • Keep hierarchy consistent across products.
  • +
  • Prefer short, clear labels.
  • +
  • Make numbers and technical values easy to scan.
  • +
  • Use mono type where alignment matters.
  • +
  • Use display variants sparingly for high-level moments.
  • +
  • Use meta for compact operational information.
  • +
  • Use overlines for lightweight grouping.
  • +
  • Keep dense UI text calm and readable.
  • +
+
+ +
+

❌ Don't

+
    +
  • Don't create new type styles screen by screen.
  • +
  • Don't use decorative typography in operational interfaces.
  • +
  • Don't use display variants as the default heading style inside applications.
  • +
  • Don't use mono type for long paragraphs.
  • +
  • Don't rely on font weight alone to show status or severity.
  • +
  • Don't mix heading styles without a clear structure.
  • +
  • Don't use caption when the content is operational metadata.
  • +
  • Don't make technical values harder to copy or compare.
  • +
+
+
+ +## Light and dark behaviour + +Typography should remain readable in both light and dark modes. + +Use semantic foreground roles rather than raw colour values: + +```tsx + + Primary text + + + + Secondary text + + + + Disabled text + +``` + +Avoid hard-coded greys for text. They may pass in one mode and fail in another. + +## Accessibility + +Typography must support legibility, contrast, and predictable reading order. + +- Use sufficient contrast for text and icons. +- Do not use colour alone to communicate meaning. +- Keep line lengths comfortable for reading. +- Avoid very small text in dense operational areas. +- Preserve semantic headings where possible. + +
diff --git a/src/storybook/foundation/3. icons.mdx b/src/storybook/foundation/3. icons.mdx new file mode 100644 index 00000000..60b62cdd --- /dev/null +++ b/src/storybook/foundation/3. icons.mdx @@ -0,0 +1,466 @@ +import { Meta } from "@storybook/blocks"; +import { + Plus, + Search, + ChevronDown, + Settings, + Trash2, + Download, + Upload, + CircleCheckBig, + Info, + TriangleAlert, + CircleX, +} from "lucide-react"; + +export const iconLabelProps = { + size: 20, + strokeWidth: 1.75, + style: { verticalAlign: "middle", marginRight: "0.5rem" }, +}; + + + +
+ +# Icons + + + Icons support recognition and scanning. They should reinforce meaning, not + replace clear language. Diamond DS uses Lucide as its primary icon family. + + +## Overview + +Diamond DS uses **Lucide** as its primary icon family. + +Lucide icons are simple, line-based icons that support dense scientific +interfaces without introducing unnecessary visual noise. + +Applications should use Lucide icons wherever possible. + +When creating bespoke icons, follow the same visual language: + +
    +
  • Line-based rather than filled.
  • +
  • Consistent stroke weights.
  • +
  • Rounded line caps and joins.
  • +
  • Simple and recognisable shapes.
  • +
  • Minimal detail at small sizes.
  • +
+ +Avoid mixing multiple icon families within the same application. + +
+

+ Material Icons may remain in existing screens during the transition. For new + components, new patterns, and new application work, use Lucide or the + exported Diamond DS icon aliases. +

+
+ +## Choosing icons + +Icons should communicate familiar concepts quickly. + +Good uses include: + +
    +
  • Navigation
  • +
  • Actions
  • +
  • Status indicators
  • +
  • Object types
  • +
  • Menus and command lists
  • +
  • Toolbars
  • +
+ +Icons should not be used purely as decoration. + +### Prefer labels over icons + +Where understanding is important, pair icons with text. + +```tsx + +``` + +Avoid relying on icons alone for unfamiliar, complex, or high-risk actions. + +## Consistency + +Use the same icon for the same concept across products. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConceptExample icon
Add + Plus +
Search + Search +
Expand + ChevronDown +
Settings + Settings +
Delete + Trash2 +
Download + Download +
Upload + Upload +
+ +Changing icon metaphors between applications increases cognitive load and reduces predictability. + +
+

+ When selecting or creating icons, follow the naming conventions used by the + underlying icon library to maintain consistency and discoverability. +

+
+ +## Icon sizes + +Diamond DS defines five standard icon sizes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Icon sizeSizeStroke weight
XS16px1.5px
SM20px1.75px
MD24px2px
LG32px2.25px
XL40px2.25px
+ +Use the smallest icon size that remains recognisable and readable. + +
+

+ The `LucideIcon` wrapper is still in development. Until it ships, import + icons directly from `lucide-react` and size them by hand using the sizes and + stroke weights above. +

+
+ +{/* +Hidden pending real icon component usage (LucideIcon, Button/IconButton icon +props, menu icon patterns) landing in the DS. Re-enable once that work ships; +the surrounding sections describe the intended shape of this guidance. + +## Using icons + +Diamond DS provides a `LucideIcon` component that wraps SVG icons and applies +consistent sizing, accessibility, and styling behaviour. + +Prefer using `LucideIcon` rather than rendering icon libraries directly. + +```tsx +import { LucideIcon } from "@diamondlightsource/sci-react-ui; +import { Search } from "lucide-react"; + + +``` + +### Why use LucideIcon? + +
    +
  • Consistent sizing.
  • Consistent stroke weights.
  • +
  • Theme integration.
  • Accessibility support.
  • +
  • Future flexibility if the underlying implementation changes.
  • +
+ +## Icons in buttons + +When using Diamond DS buttons, provide a Lucide icon and allow the button +component to determine the appropriate icon size. + +```tsx + +``` + +Do not manually size icons inside standard button components unless there is a +specific reusable requirement. + +Button components automatically determine icon size. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Button sizeButton heightDefault icon size
xs24pxXS (16px)
small32pxSM (20px)
medium36pxMD (24px)
large40pxMD (24px)
+ +Do not manually resize icons inside standard button components. + +### Leading icons + +Use leading icons when the icon reinforces the primary action. + +```tsx + +``` + +### Trailing icons + +Use trailing icons when the icon indicates progression, navigation, or expansion. + +```tsx + +``` + +## Icon-only buttons + +Use icon-only buttons when: + +
    +
  • Space is limited.
  • +
  • The action is widely understood.
  • +
  • The icon remains clear without text.
  • +
+ +Every icon-only button must provide an accessible name. + +```tsx + + + +``` + +Common examples include: + +
    +
  • Search
  • +
  • Settings
  • +
  • Close
  • +
  • More actions
  • +
  • Notifications
  • +
+ +## Icons in menus + +Icons can improve scanning within menus containing multiple actions. + +```tsx + + + + + + Settings + +``` + +Use icons consistently throughout a menu. + +Avoid mixing icon and non-icon menu items without a clear reason. +*/} + +## Icons in navigation + +Icons may support navigation but should rarely replace labels in desktop scientific applications. + +Navigation labels should remain visible wherever space allows. + +Icons are most useful for: + +
    +
  • Application launchers
  • +
  • Navigation rails
  • +
  • Collapsed side navigation
  • +
  • Toolbar actions
  • +
+ +## Status and feedback + +Icons can reinforce status messages but should not be the only indicator. + + + + + + + + + + + + + + + + + + + + + + + + + + +
StatusTypical icon
Success + CircleCheckBig +
Information + Info +
Warning + TriangleAlert +
Error + CircleX +
+ +Always combine icons with text and colour. + +## Accessibility + +### Decorative icons + +Hide decorative icons from assistive technologies. + +```tsx +