Files
sharper.ly/apps/web/app/styles/typography.css
T
2026-09-11 02:46:56 +02:00

225 lines
7.7 KiB
CSS

/* ============================================================================
TYPE ROLES (tier 2 for typography)
----------------------------------------------------------------------------
Composes tier-1 primitives into the named roles published in Figma
(Display/Small, Heading/H2, Body/*, Label/Medium) plus the two headings the
scale needs but Figma has not drawn yet (H1, H3), derived from the same ramp.
Components use the utilities below - `display-sm`, `heading-h2`, `body-md`,
`label-md` - never a raw font-size / line-height / letter-spacing.
Panton (EN) and Montserrat Arabic (AR) are the brand faces, both supplied
and present in apps/web/public/fonts/. Licensed to the project, not to this
repository - .gitignore keeps every woff2 out of version control.
See ./README.md for the filename of every face.
========================================================================= */
/* ---------------------------------------------------------------------------
@font-face - Panton (English / Latin)
Regular and Bold are supplied and live in public/fonts/. A SemiBold cut has
not been supplied, so Label/Medium (weight 500) resolves to Panton Regular
through normal CSS font matching - see ./README.md to upgrade it.
------------------------------------------------------------------------ */
@font-face {
font-family: "Panton";
src: url("/fonts/Panton-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Panton";
src: url("/fonts/Panton-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* ---------------------------------------------------------------------------
@font-face - Arabic
"Montserrat Arabic" is THE Arabic brand face. The communication guideline
PDF names "Montaser Arabic", but that is a typo in the guideline - confirmed
with the client on 2026-09-11. Do not reintroduce a "Montaser Arabic"
family: it does not exist and would silently shadow the real one, because it
would sit ahead of Montserrat in --font-ar and match any similarly-named
face a visitor happens to have installed.
Three weights are supplied in public/fonts/. unicode-range keeps every
Arabic file off English pages entirely.
------------------------------------------------------------------------ */
@font-face {
font-family: "Montserrat Arabic";
src: url("/fonts/Montserrat-Arabic-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF,
U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41,
U+0660-0669;
}
@font-face {
font-family: "Montserrat Arabic";
src: url("/fonts/Montserrat-Arabic-Medium.woff2") format("woff2");
font-weight: 500 600;
font-style: normal;
font-display: swap;
unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF,
U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41,
U+0660-0669;
}
@font-face {
font-family: "Montserrat Arabic";
src: url("/fonts/Montserrat-Arabic-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF,
U+FB50-FDFF, U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41,
U+0660-0669;
}
/* ---------------------------------------------------------------------------
TYPE SEMANTIC TOKENS
The EN/AR switch is a token rebind, not a second set of utilities: every
role below reads --font-heading / --font-body and the role tracking tokens,
so `<html lang="ar">` - or any Arabic subtree - re-points them once and the
whole scale follows.
Latin tracking is negative on display sizes and positive on labels; Arabic
is a joined script where letter-spacing breaks the connections, so all
Arabic tracking resets to normal.
------------------------------------------------------------------------ */
:root {
--font-heading: var(--font-en);
--font-body: var(--font-en);
--font-ui: var(--font-en);
--type-tracking-display: var(--tracking-tighter);
--type-tracking-h1: var(--tracking-tight);
--type-tracking-h2: var(--tracking-snug);
--type-tracking-h3: var(--tracking-snug);
--type-tracking-body: var(--tracking-normal);
--type-tracking-label: var(--tracking-wide);
}
[lang="ar"],
:lang(ar) {
--font-heading: var(--font-ar);
--font-body: var(--font-ar);
--font-ui: var(--font-ar);
--type-tracking-display: var(--tracking-normal);
--type-tracking-h1: var(--tracking-normal);
--type-tracking-h2: var(--tracking-normal);
--type-tracking-h3: var(--tracking-normal);
--type-tracking-body: var(--tracking-normal);
--type-tracking-label: var(--tracking-normal);
}
/* ---------------------------------------------------------------------------
TYPE ROLE UTILITIES
------------------------------------------------------------------------ */
/* Display/Small - Bold 44/52, ls -1 [figma] */
@utility display-sm {
font-family: var(--font-heading);
font-size: var(--text-4xl);
line-height: var(--leading-11);
letter-spacing: var(--type-tracking-display);
font-weight: var(--font-weight-bold);
}
/* Heading/H1 - Bold 36/44 (derived - not yet drawn in Figma) */
@utility heading-h1 {
font-family: var(--font-heading);
font-size: var(--text-3xl);
line-height: var(--leading-10);
letter-spacing: var(--type-tracking-h1);
font-weight: var(--font-weight-bold);
}
/* Heading/H2 - Bold 28/36, ls -0.3 [figma] */
@utility heading-h2 {
font-family: var(--font-heading);
font-size: var(--text-2xl);
line-height: var(--leading-9);
letter-spacing: var(--type-tracking-h2);
font-weight: var(--font-weight-bold);
}
/* Heading/H3 - Bold 22/30 (derived - not yet drawn in Figma) */
@utility heading-h3 {
font-family: var(--font-heading);
font-size: var(--text-xl);
line-height: var(--leading-8);
letter-spacing: var(--type-tracking-h3);
font-weight: var(--font-weight-bold);
}
/* Body/Large - 18/28 */
@utility body-lg {
font-family: var(--font-body);
font-size: var(--text-lg);
line-height: var(--leading-7);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-regular);
}
/* Body/Large Bold - Bold 18/28 [figma] */
@utility body-lg-bold {
font-family: var(--font-body);
font-size: var(--text-lg);
line-height: var(--leading-7);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-bold);
}
/* Body/Medium - 16/24 */
@utility body-md {
font-family: var(--font-body);
font-size: var(--text-md);
line-height: var(--leading-6);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-regular);
}
/* Body/Medium Bold - Bold 16/24 [figma] */
@utility body-md-bold {
font-family: var(--font-body);
font-size: var(--text-md);
line-height: var(--leading-6);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-bold);
}
/* Body/Small - Regular 14/20 [figma] */
@utility body-sm {
font-family: var(--font-body);
font-size: var(--text-sm);
line-height: var(--leading-5);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-regular);
}
/* Body/Small Bold - Bold 14/20 [figma] */
@utility body-sm-bold {
font-family: var(--font-body);
font-size: var(--text-sm);
line-height: var(--leading-5);
letter-spacing: var(--type-tracking-body);
font-weight: var(--font-weight-bold);
}
/* Label/Medium - SemiBold 12/16, ls 0.5 [figma] */
@utility label-md {
font-family: var(--font-ui);
font-size: var(--text-xs);
line-height: var(--leading-4);
letter-spacing: var(--type-tracking-label);
font-weight: var(--font-weight-medium);
}