@motir/design-system 0.15.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/theme/StyleVignette.js +3 -3
- package/dist/components/theme/StyleVignette.js.map +1 -1
- package/dist/components/ui/Combobox.js +2 -2
- package/dist/components/ui/Combobox.js.map +1 -1
- package/dist/components/ui/MultiSelectPicker.js +1 -1
- package/dist/components/ui/MultiSelectPicker.js.map +1 -1
- package/dist/contexts/theme-context.js +25 -1
- package/dist/contexts/theme-context.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/specimen/TokensSpecimen.js +3 -3
- package/dist/specimen/TokensSpecimen.js.map +1 -1
- package/dist/theme/fontSets.d.ts +38 -5
- package/dist/theme/fontSets.js +33 -3
- package/dist/theme/fontSets.js.map +1 -1
- package/package.json +1 -1
- package/theme.css +93 -0
package/dist/theme/fontSets.d.ts
CHANGED
|
@@ -24,8 +24,16 @@
|
|
|
24
24
|
*/
|
|
25
25
|
/** The eleven locales app.motir.co ships in. A closed product decision. */
|
|
26
26
|
declare const FONT_SET_LOCALES: readonly ["en", "zh", "ja", "ko", "de", "fr", "es", "it", "nl", "pl", "pt"];
|
|
27
|
-
/**
|
|
27
|
+
/** One of the eleven app locales. */
|
|
28
28
|
type FontSetLocale = (typeof FONT_SET_LOCALES)[number];
|
|
29
|
+
/**
|
|
30
|
+
* Languages with a font set that only motir.co ships (MOTIR-8937 · MOTIR-9239).
|
|
31
|
+
* Kept apart from `FONT_SET_LOCALES`, which is app.motir.co's own locale set and
|
|
32
|
+
* is closed over its stored font picks (one `fontPick<Locale>` column each).
|
|
33
|
+
*/
|
|
34
|
+
declare const FONT_SET_EXTRA_LOCALES: readonly ["zh-tw"];
|
|
35
|
+
/** A language the font-set registry knows: an app locale or an extra one. */
|
|
36
|
+
type FontSetLanguage = FontSetLocale | (typeof FONT_SET_EXTRA_LOCALES)[number];
|
|
29
37
|
/** The three type roles every set covers: the same roles `--font-*` carries. */
|
|
30
38
|
declare const FONT_SET_ROLES: readonly ["sans", "serif", "mono"];
|
|
31
39
|
/** One of the three type roles. */
|
|
@@ -128,6 +136,26 @@ declare const FONT_SET_REGISTRY: {
|
|
|
128
136
|
};
|
|
129
137
|
};
|
|
130
138
|
};
|
|
139
|
+
readonly 'zh-Hant': {
|
|
140
|
+
readonly id: "zh-Hant";
|
|
141
|
+
readonly label: "Traditional Chinese";
|
|
142
|
+
readonly lang: "zh-TW";
|
|
143
|
+
readonly cjk: true;
|
|
144
|
+
readonly roles: {
|
|
145
|
+
readonly sans: {
|
|
146
|
+
readonly default: "noto-sans-tc";
|
|
147
|
+
readonly members: readonly [FontSetMember];
|
|
148
|
+
};
|
|
149
|
+
readonly serif: {
|
|
150
|
+
readonly default: "noto-serif-tc";
|
|
151
|
+
readonly members: readonly [FontSetMember, FontSetMember];
|
|
152
|
+
};
|
|
153
|
+
readonly mono: {
|
|
154
|
+
readonly default: "noto-sans-tc";
|
|
155
|
+
readonly members: readonly [FontSetMember];
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
};
|
|
131
159
|
readonly ja: {
|
|
132
160
|
readonly id: "ja";
|
|
133
161
|
readonly label: "Japanese";
|
|
@@ -188,12 +216,17 @@ declare const LOCALE_FONT_SET: {
|
|
|
188
216
|
readonly nl: "latin";
|
|
189
217
|
readonly pl: "latin";
|
|
190
218
|
readonly pt: "latin";
|
|
219
|
+
readonly 'zh-tw': "zh-Hant";
|
|
191
220
|
};
|
|
192
221
|
/** Narrowing guard: is an arbitrary value one of the eleven locales? */
|
|
193
222
|
declare function isFontSetLocale(value: unknown): value is FontSetLocale;
|
|
223
|
+
/** Narrowing guard: is an arbitrary value an app locale or an extra language? */
|
|
224
|
+
declare function isFontSetLanguage(value: unknown): value is FontSetLanguage;
|
|
194
225
|
/**
|
|
195
|
-
* The set for an `<html lang>` value.
|
|
196
|
-
*
|
|
226
|
+
* The set for an `<html lang>` value. Tries the whole tag first, case-insensitively
|
|
227
|
+
* and reading `_` as `-` (`zh-TW` / `zh_tw` → zh-Hant), then the primary subtag
|
|
228
|
+
* (`ja-JP` → ja, `PT-br` → pt, `zh-Hans` → zh). No script or region heuristics:
|
|
229
|
+
* `zh-Hant` and `zh-HK` reach zh-Hant only through a page whose `lang` is `zh-TW`.
|
|
197
230
|
*
|
|
198
231
|
* The fallback is explicit: an empty or unknown tag returns the Latin set,
|
|
199
232
|
* because the app renders an unknown language in English. It never falls back
|
|
@@ -232,6 +265,6 @@ type FontSetPickAttribute = `data-font-set-${FontSetRole}`;
|
|
|
232
265
|
* first byte share, so a pick sets identical attributes on both sides — which
|
|
233
266
|
* is why it lives here, free of any `'use client'` boundary.
|
|
234
267
|
*/
|
|
235
|
-
declare function fontSetPickAttributes(locale:
|
|
268
|
+
declare function fontSetPickAttributes(locale: FontSetLanguage, memberId: string | null): Partial<Record<FontSetPickAttribute, string>>;
|
|
236
269
|
|
|
237
|
-
export { DEFAULT_FONT_SET_ID, FONT_SET_IDS, FONT_SET_LOCALES, FONT_SET_REGISTRY, FONT_SET_ROLES, type FontSet, type FontSetId, type FontSetLocale, type FontSetMember, type FontSetMemberSource, type FontSetPickAttribute, type FontSetRole, type FontSetRoleMembers, LOCALE_FONT_SET, fontSetMemberVar, fontSetPickAttributes, isFontSetLocale, resolveFontSet, resolveFontSetMember };
|
|
270
|
+
export { DEFAULT_FONT_SET_ID, FONT_SET_EXTRA_LOCALES, FONT_SET_IDS, FONT_SET_LOCALES, FONT_SET_REGISTRY, FONT_SET_ROLES, type FontSet, type FontSetId, type FontSetLanguage, type FontSetLocale, type FontSetMember, type FontSetMemberSource, type FontSetPickAttribute, type FontSetRole, type FontSetRoleMembers, LOCALE_FONT_SET, fontSetMemberVar, fontSetPickAttributes, isFontSetLanguage, isFontSetLocale, resolveFontSet, resolveFontSetMember };
|
package/dist/theme/fontSets.js
CHANGED
|
@@ -12,6 +12,7 @@ var FONT_SET_LOCALES = [
|
|
|
12
12
|
"pl",
|
|
13
13
|
"pt"
|
|
14
14
|
];
|
|
15
|
+
var FONT_SET_EXTRA_LOCALES = ["zh-tw"];
|
|
15
16
|
var FONT_SET_ROLES = ["sans", "serif", "mono"];
|
|
16
17
|
var TYPE_PAIRING = {
|
|
17
18
|
id: "type-pairing",
|
|
@@ -64,6 +65,29 @@ var FONT_SET_REGISTRY = {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
},
|
|
68
|
+
// Not in dooooWeb: Traditional Chinese (Taiwan) for motir.co's `/zh-tw`
|
|
69
|
+
// (MOTIR-8937). The zh-Hans set with its Traditional-form faces.
|
|
70
|
+
"zh-Hant": {
|
|
71
|
+
id: "zh-Hant",
|
|
72
|
+
label: "Traditional Chinese",
|
|
73
|
+
lang: "zh-TW",
|
|
74
|
+
cjk: true,
|
|
75
|
+
roles: {
|
|
76
|
+
sans: { default: "noto-sans-tc", members: [google("noto-sans-tc", "Noto Sans TC", false)] },
|
|
77
|
+
serif: {
|
|
78
|
+
default: "noto-serif-tc",
|
|
79
|
+
members: [
|
|
80
|
+
google("noto-serif-tc", "Noto Serif TC", false),
|
|
81
|
+
// A handwritten-style face, so a pick rather than the default.
|
|
82
|
+
google("lxgw-wenkai-tc", "LXGW WenKai TC", false)
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
mono: {
|
|
86
|
+
default: "noto-sans-tc",
|
|
87
|
+
members: [google("noto-sans-tc", "Noto Sans TC", false, "sans")]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
67
91
|
// dooooWeb: `japanese`.
|
|
68
92
|
ja: {
|
|
69
93
|
id: "ja",
|
|
@@ -126,13 +150,19 @@ var LOCALE_FONT_SET = {
|
|
|
126
150
|
it: "latin",
|
|
127
151
|
nl: "latin",
|
|
128
152
|
pl: "latin",
|
|
129
|
-
pt: "latin"
|
|
153
|
+
pt: "latin",
|
|
154
|
+
"zh-tw": "zh-Hant"
|
|
130
155
|
};
|
|
131
156
|
function isFontSetLocale(value) {
|
|
132
157
|
return typeof value === "string" && FONT_SET_LOCALES.includes(value);
|
|
133
158
|
}
|
|
159
|
+
function isFontSetLanguage(value) {
|
|
160
|
+
return isFontSetLocale(value) || typeof value === "string" && FONT_SET_EXTRA_LOCALES.includes(value);
|
|
161
|
+
}
|
|
134
162
|
function resolveFontSet(lang) {
|
|
135
|
-
const
|
|
163
|
+
const tag = lang.trim().toLowerCase().replace(/_/g, "-");
|
|
164
|
+
if (isFontSetLanguage(tag)) return FONT_SET_REGISTRY[LOCALE_FONT_SET[tag]];
|
|
165
|
+
const primary = tag.split("-")[0] ?? "";
|
|
136
166
|
const id = isFontSetLocale(primary) ? LOCALE_FONT_SET[primary] : DEFAULT_FONT_SET_ID;
|
|
137
167
|
return FONT_SET_REGISTRY[id];
|
|
138
168
|
}
|
|
@@ -162,6 +192,6 @@ function fontSetPickAttributes(locale, memberId) {
|
|
|
162
192
|
return attrs;
|
|
163
193
|
}
|
|
164
194
|
|
|
165
|
-
export { DEFAULT_FONT_SET_ID, FONT_SET_IDS, FONT_SET_LOCALES, FONT_SET_REGISTRY, FONT_SET_ROLES, LOCALE_FONT_SET, fontSetMemberVar, fontSetPickAttributes, isFontSetLocale, resolveFontSet, resolveFontSetMember };
|
|
195
|
+
export { DEFAULT_FONT_SET_ID, FONT_SET_EXTRA_LOCALES, FONT_SET_IDS, FONT_SET_LOCALES, FONT_SET_REGISTRY, FONT_SET_ROLES, LOCALE_FONT_SET, fontSetMemberVar, fontSetPickAttributes, isFontSetLanguage, isFontSetLocale, resolveFontSet, resolveFontSetMember };
|
|
166
196
|
//# sourceMappingURL=fontSets.js.map
|
|
167
197
|
//# sourceMappingURL=fontSets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/theme/fontSets.ts"],"names":[],"mappings":"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","file":"fontSets.js","sourcesContent":["/**\n * The Motir FONT-SET REGISTRY: which faces each locale's script is drawn in\n * (Story MOTIR-7733 · Subtask MOTIR-7843).\n *\n * A Type pairing (`./typography.ts`) decides the LATIN faces of the three roles\n * (sans / serif / mono). A font set decides the SCRIPT face that sits BEHIND\n * them, so a ja page draws its Latin in the pairing's face and its kana and\n * kanji in the ja set's face, under every pairing. The two axes are\n * independent: a set never changes which pairing is active, and a pairing never\n * changes which set a locale uses.\n *\n * ── Where the rows come from ────────────────────────────────────────────\n * Every set, member and default below is taken from\n * `docs/typography/font-sets.md` (MOTIR-7841), which read dooooWeb's mapping at\n * `225af77` and filled the roles dooooWeb leaves empty from cited references.\n * `inDooooWeb` carries that document's `in dooooWeb` / `added (not in dooooWeb)`\n * tag. Change a row here only together with that document; the package test\n * holds the two equal.\n *\n * ── What this module is NOT ─────────────────────────────────────────────\n * Data and pure resolvers only. It names CSS custom properties (the contract\n * between the app's `next/font` loader and `theme.css`) but sets none, loads no\n * face, and writes no CSS. Loading stays in the consuming app.\n */\n\n/** The eleven locales app.motir.co ships in. A closed product decision. */\nexport const FONT_SET_LOCALES = [\n 'en',\n 'zh',\n 'ja',\n 'ko',\n 'de',\n 'fr',\n 'es',\n 'it',\n 'nl',\n 'pl',\n 'pt',\n] as const;\n\n/** A locale the font-set registry knows. */\nexport type FontSetLocale = (typeof FONT_SET_LOCALES)[number];\n\n/** The three type roles every set covers: the same roles `--font-*` carries. */\nexport const FONT_SET_ROLES = ['sans', 'serif', 'mono'] as const;\n\n/** One of the three type roles. */\nexport type FontSetRole = (typeof FONT_SET_ROLES)[number];\n\n/**\n * Where a member's face comes from.\n *\n * - `type-pairing`: the active Type pairing's own face for the role. The Latin\n * set's only member; it loads nothing and has no CSS variable.\n * - `next/font/google`: a Google Fonts face the app loads with `next/font`;\n * `googleFamily` is the loader's export name (`Noto_Sans_JP`).\n */\nexport type FontSetMemberSource =\n | { readonly kind: 'type-pairing' }\n | { readonly kind: 'next/font/google'; readonly googleFamily: string };\n\n/** One selectable face in one role of one set. */\nexport interface FontSetMember {\n /** Stable kebab-case slug, unique within its role. The value a pick stores. */\n readonly id: string;\n /** The display name, or `null` for the `type-pairing` member. */\n readonly family: string | null;\n readonly source: FontSetMemberSource;\n /** `docs/typography/font-sets.md`'s tag: `in dooooWeb` (true) or `added` (false). */\n readonly inDooooWeb: boolean;\n /**\n * The role whose member of the SAME id this member re-uses the face of. A\n * CJK set's mono role draws its Han glyphs from the set's sans face (the Noto\n * Sans Mono CJK practice, see the doc), so the face is loaded once and both\n * roles read one variable.\n */\n readonly sameFaceAs?: FontSetRole;\n}\n\n/** One role of one set: its members, and which one a page uses by default. */\nexport interface FontSetRoleMembers {\n /** The id of the member used when nobody has picked one. */\n readonly default: string;\n readonly members: readonly FontSetMember[];\n}\n\n/** A locale group's font set. */\nexport interface FontSet {\n readonly id: string;\n /** Display name for `/tokens` and the picker. */\n readonly label: string;\n /**\n * The BCP 47 tag the set's `:lang()` rule matches. It is the tag the app\n * renders on `<html lang>` (`zh`, not `zh-Hans`), because `:lang(zh)`\n * matches `zh` and `zh-Hans` while `:lang(zh-Hans)` does not match `zh`.\n * `null` for the Latin set, which is matched by its locales instead.\n */\n readonly lang: string | null;\n /** True for a CJK set: its faces are unicode-range sliced and never preloaded. */\n readonly cjk: boolean;\n readonly roles: Readonly<Record<FontSetRole, FontSetRoleMembers>>;\n}\n\nconst TYPE_PAIRING: FontSetMember = {\n id: 'type-pairing',\n family: null,\n source: { kind: 'type-pairing' },\n inDooooWeb: false,\n};\n\nfunction google(\n id: string,\n family: string,\n inDooooWeb: boolean,\n sameFaceAs?: FontSetRole,\n): FontSetMember {\n return {\n id,\n family,\n source: { kind: 'next/font/google', googleFamily: family.replace(/ /g, '_') },\n inDooooWeb,\n ...(sameFaceAs ? { sameFaceAs } : {}),\n };\n}\n\n/**\n * The registry, one entry per set. Ids are the doc's registry ids; dooooWeb's\n * own group names are noted beside each.\n */\nexport const FONT_SET_REGISTRY = {\n // dooooWeb: `latin`. Its Latin picker faces are deliberately not members:\n // behind the pairing's face they would never draw a glyph (font-sets.md § latin).\n latin: {\n id: 'latin',\n label: 'Latin',\n lang: null,\n cjk: false,\n roles: {\n sans: { default: 'type-pairing', members: [TYPE_PAIRING] },\n serif: { default: 'type-pairing', members: [TYPE_PAIRING] },\n mono: { default: 'type-pairing', members: [TYPE_PAIRING] },\n },\n },\n // dooooWeb: `chinese-simplified`.\n 'zh-Hans': {\n id: 'zh-Hans',\n label: 'Simplified Chinese',\n lang: 'zh',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-sc', members: [google('noto-sans-sc', 'Noto Sans SC', false)] },\n serif: {\n default: 'noto-serif-sc',\n members: [\n google('noto-serif-sc', 'Noto Serif SC', true),\n // Draws Traditional / inherited forms, so never the default (font-sets.md § Region forms).\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', true),\n ],\n },\n mono: {\n default: 'noto-sans-sc',\n members: [google('noto-sans-sc', 'Noto Sans SC', false, 'sans')],\n },\n },\n },\n // dooooWeb: `japanese`.\n ja: {\n id: 'ja',\n label: 'Japanese',\n lang: 'ja',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-jp',\n members: [\n google('noto-sans-jp', 'Noto Sans JP', true),\n google('m-plus-rounded-1c', 'M PLUS Rounded 1c', true),\n ],\n },\n serif: {\n default: 'noto-serif-jp',\n members: [google('noto-serif-jp', 'Noto Serif JP', false)],\n },\n mono: {\n default: 'noto-sans-jp',\n members: [google('noto-sans-jp', 'Noto Sans JP', false, 'sans')],\n },\n },\n },\n // dooooWeb: `korean`.\n ko: {\n id: 'ko',\n label: 'Korean',\n lang: 'ko',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-kr',\n members: [\n google('noto-sans-kr', 'Noto Sans KR', true),\n google('nanum-gothic', 'Nanum Gothic', true),\n ],\n },\n serif: {\n default: 'noto-serif-kr',\n members: [google('noto-serif-kr', 'Noto Serif KR', false)],\n },\n mono: {\n default: 'noto-sans-kr',\n members: [google('noto-sans-kr', 'Noto Sans KR', false, 'sans')],\n },\n },\n },\n} as const satisfies Record<string, FontSet>;\n\n/** The id of every registered set. */\nexport type FontSetId = keyof typeof FONT_SET_REGISTRY;\n\n/** All set ids, in registry order. */\nexport const FONT_SET_IDS = Object.keys(FONT_SET_REGISTRY) as FontSetId[];\n\n/** The set an unknown or empty page language falls back to: the app renders it in English. */\nexport const DEFAULT_FONT_SET_ID: FontSetId = 'latin';\n\n/** Which set each locale is drawn in (font-sets.md § Locale → set). */\nexport const LOCALE_FONT_SET = {\n en: 'latin',\n zh: 'zh-Hans',\n ja: 'ja',\n ko: 'ko',\n de: 'latin',\n fr: 'latin',\n es: 'latin',\n it: 'latin',\n nl: 'latin',\n pl: 'latin',\n pt: 'latin',\n} as const satisfies Record<FontSetLocale, FontSetId>;\n\n/** Narrowing guard: is an arbitrary value one of the eleven locales? */\nexport function isFontSetLocale(value: unknown): value is FontSetLocale {\n return typeof value === 'string' && (FONT_SET_LOCALES as readonly string[]).includes(value);\n}\n\n/**\n * The set for an `<html lang>` value. Matches the primary subtag\n * case-insensitively (`ja-JP` → ja, `PT-br` → pt, `zh-Hans` → zh).\n *\n * The fallback is explicit: an empty or unknown tag returns the Latin set,\n * because the app renders an unknown language in English. It never falls back\n * to \"the nearest CJK set\". Use `isFontSetLocale` on the primary subtag to tell\n * a fallback from a match.\n */\nexport function resolveFontSet(lang: string): FontSet {\n const primary = lang.trim().split(/[-_]/)[0]?.toLowerCase() ?? '';\n const id = isFontSetLocale(primary) ? LOCALE_FONT_SET[primary] : DEFAULT_FONT_SET_ID;\n return FONT_SET_REGISTRY[id];\n}\n\n/**\n * A member of one role of one set, by id. A missing or unknown id (a stored\n * pick naming a member that has since been removed) returns that role's\n * default, never another set's face.\n */\nexport function resolveFontSetMember(\n setId: FontSetId,\n role: FontSetRole,\n memberId?: string,\n): FontSetMember {\n const r: FontSetRoleMembers = FONT_SET_REGISTRY[setId].roles[role];\n const named = memberId === undefined ? undefined : r.members.find((m) => m.id === memberId);\n return named ?? r.members.find((m) => m.id === r.default)!;\n}\n\n/**\n * The CSS custom property the app's `next/font` loader assigns to a member's\n * face and `theme.css` reads: `--font-set-<set>-<role>-<member>`. This name is\n * the contract between the registry, the loader and the stylesheet, so it is\n * built here and nowhere else.\n *\n * A member that re-uses another role's face (`sameFaceAs`) returns THAT role's\n * variable, so the face is declared once. The `type-pairing` member, and an id\n * the role does not have, return `null`.\n */\nexport function fontSetMemberVar(\n setId: FontSetId,\n role: FontSetRole,\n memberId: string,\n): string | null {\n const member = FONT_SET_REGISTRY[setId].roles[role].members.find(\n (m: FontSetMember) => m.id === memberId,\n );\n if (!member || member.source.kind === 'type-pairing') return null;\n const faceRole = member.sameFaceAs ?? role;\n return `--font-set-${setId}-${faceRole}-${member.id}`;\n}\n\n/** The `<html>` attribute that names a picked member for one role. */\nexport type FontSetPickAttribute = `data-font-set-${FontSetRole}`;\n\n/**\n * The `data-font-set-<role>` attributes a person's pick for `locale` puts on\n * `<html>` (Story MOTIR-7736 · MOTIR-7897). One attribute per role of the\n * locale's set that has a member with this id, unless that member is the\n * `type-pairing` placeholder or the role's default: `theme.css` carries a rule\n * only for a non-default member, so a default needs no attribute.\n *\n * `null`, an unknown id, a default id and every Latin locale return `{}`. This\n * is the ONE derivation the client (`setFontPick`) and the server-rendered\n * first byte share, so a pick sets identical attributes on both sides — which\n * is why it lives here, free of any `'use client'` boundary.\n */\nexport function fontSetPickAttributes(\n locale: FontSetLocale,\n memberId: string | null,\n): Partial<Record<FontSetPickAttribute, string>> {\n if (memberId === null) return {};\n const set: FontSet = FONT_SET_REGISTRY[LOCALE_FONT_SET[locale]];\n const attrs: Partial<Record<FontSetPickAttribute, string>> = {};\n for (const role of FONT_SET_ROLES) {\n const r: FontSetRoleMembers = set.roles[role];\n if (memberId === r.default) continue;\n const member = r.members.find((m: FontSetMember) => m.id === memberId);\n if (member && member.source.kind !== 'type-pairing') attrs[`data-font-set-${role}`] = memberId;\n }\n return attrs;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/theme/fontSets.ts"],"names":[],"mappings":"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","file":"fontSets.js","sourcesContent":["/**\n * The Motir FONT-SET REGISTRY: which faces each locale's script is drawn in\n * (Story MOTIR-7733 · Subtask MOTIR-7843).\n *\n * A Type pairing (`./typography.ts`) decides the LATIN faces of the three roles\n * (sans / serif / mono). A font set decides the SCRIPT face that sits BEHIND\n * them, so a ja page draws its Latin in the pairing's face and its kana and\n * kanji in the ja set's face, under every pairing. The two axes are\n * independent: a set never changes which pairing is active, and a pairing never\n * changes which set a locale uses.\n *\n * ── Where the rows come from ────────────────────────────────────────────\n * Every set, member and default below is taken from\n * `docs/typography/font-sets.md` (MOTIR-7841), which read dooooWeb's mapping at\n * `225af77` and filled the roles dooooWeb leaves empty from cited references.\n * `inDooooWeb` carries that document's `in dooooWeb` / `added (not in dooooWeb)`\n * tag. Change a row here only together with that document; the package test\n * holds the two equal.\n *\n * ── What this module is NOT ─────────────────────────────────────────────\n * Data and pure resolvers only. It names CSS custom properties (the contract\n * between the app's `next/font` loader and `theme.css`) but sets none, loads no\n * face, and writes no CSS. Loading stays in the consuming app.\n */\n\n/** The eleven locales app.motir.co ships in. A closed product decision. */\nexport const FONT_SET_LOCALES = [\n 'en',\n 'zh',\n 'ja',\n 'ko',\n 'de',\n 'fr',\n 'es',\n 'it',\n 'nl',\n 'pl',\n 'pt',\n] as const;\n\n/** One of the eleven app locales. */\nexport type FontSetLocale = (typeof FONT_SET_LOCALES)[number];\n\n/**\n * Languages with a font set that only motir.co ships (MOTIR-8937 · MOTIR-9239).\n * Kept apart from `FONT_SET_LOCALES`, which is app.motir.co's own locale set and\n * is closed over its stored font picks (one `fontPick<Locale>` column each).\n */\nexport const FONT_SET_EXTRA_LOCALES = ['zh-tw'] as const;\n\n/** A language the font-set registry knows: an app locale or an extra one. */\nexport type FontSetLanguage = FontSetLocale | (typeof FONT_SET_EXTRA_LOCALES)[number];\n\n/** The three type roles every set covers: the same roles `--font-*` carries. */\nexport const FONT_SET_ROLES = ['sans', 'serif', 'mono'] as const;\n\n/** One of the three type roles. */\nexport type FontSetRole = (typeof FONT_SET_ROLES)[number];\n\n/**\n * Where a member's face comes from.\n *\n * - `type-pairing`: the active Type pairing's own face for the role. The Latin\n * set's only member; it loads nothing and has no CSS variable.\n * - `next/font/google`: a Google Fonts face the app loads with `next/font`;\n * `googleFamily` is the loader's export name (`Noto_Sans_JP`).\n */\nexport type FontSetMemberSource =\n | { readonly kind: 'type-pairing' }\n | { readonly kind: 'next/font/google'; readonly googleFamily: string };\n\n/** One selectable face in one role of one set. */\nexport interface FontSetMember {\n /** Stable kebab-case slug, unique within its role. The value a pick stores. */\n readonly id: string;\n /** The display name, or `null` for the `type-pairing` member. */\n readonly family: string | null;\n readonly source: FontSetMemberSource;\n /** `docs/typography/font-sets.md`'s tag: `in dooooWeb` (true) or `added` (false). */\n readonly inDooooWeb: boolean;\n /**\n * The role whose member of the SAME id this member re-uses the face of. A\n * CJK set's mono role draws its Han glyphs from the set's sans face (the Noto\n * Sans Mono CJK practice, see the doc), so the face is loaded once and both\n * roles read one variable.\n */\n readonly sameFaceAs?: FontSetRole;\n}\n\n/** One role of one set: its members, and which one a page uses by default. */\nexport interface FontSetRoleMembers {\n /** The id of the member used when nobody has picked one. */\n readonly default: string;\n readonly members: readonly FontSetMember[];\n}\n\n/** A locale group's font set. */\nexport interface FontSet {\n readonly id: string;\n /** Display name for `/tokens` and the picker. */\n readonly label: string;\n /**\n * The BCP 47 tag the set's `:lang()` rule matches. It is the tag the app\n * renders on `<html lang>` (`zh`, not `zh-Hans`), because `:lang(zh)`\n * matches `zh` and `zh-Hans` while `:lang(zh-Hans)` does not match `zh`.\n * `null` for the Latin set, which is matched by its locales instead.\n */\n readonly lang: string | null;\n /** True for a CJK set: its faces are unicode-range sliced and never preloaded. */\n readonly cjk: boolean;\n readonly roles: Readonly<Record<FontSetRole, FontSetRoleMembers>>;\n}\n\nconst TYPE_PAIRING: FontSetMember = {\n id: 'type-pairing',\n family: null,\n source: { kind: 'type-pairing' },\n inDooooWeb: false,\n};\n\nfunction google(\n id: string,\n family: string,\n inDooooWeb: boolean,\n sameFaceAs?: FontSetRole,\n): FontSetMember {\n return {\n id,\n family,\n source: { kind: 'next/font/google', googleFamily: family.replace(/ /g, '_') },\n inDooooWeb,\n ...(sameFaceAs ? { sameFaceAs } : {}),\n };\n}\n\n/**\n * The registry, one entry per set. Ids are the doc's registry ids; dooooWeb's\n * own group names are noted beside each.\n */\nexport const FONT_SET_REGISTRY = {\n // dooooWeb: `latin`. Its Latin picker faces are deliberately not members:\n // behind the pairing's face they would never draw a glyph (font-sets.md § latin).\n latin: {\n id: 'latin',\n label: 'Latin',\n lang: null,\n cjk: false,\n roles: {\n sans: { default: 'type-pairing', members: [TYPE_PAIRING] },\n serif: { default: 'type-pairing', members: [TYPE_PAIRING] },\n mono: { default: 'type-pairing', members: [TYPE_PAIRING] },\n },\n },\n // dooooWeb: `chinese-simplified`.\n 'zh-Hans': {\n id: 'zh-Hans',\n label: 'Simplified Chinese',\n lang: 'zh',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-sc', members: [google('noto-sans-sc', 'Noto Sans SC', false)] },\n serif: {\n default: 'noto-serif-sc',\n members: [\n google('noto-serif-sc', 'Noto Serif SC', true),\n // Draws Traditional / inherited forms, so never the default (font-sets.md § Region forms).\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', true),\n ],\n },\n mono: {\n default: 'noto-sans-sc',\n members: [google('noto-sans-sc', 'Noto Sans SC', false, 'sans')],\n },\n },\n },\n // Not in dooooWeb: Traditional Chinese (Taiwan) for motir.co's `/zh-tw`\n // (MOTIR-8937). The zh-Hans set with its Traditional-form faces.\n 'zh-Hant': {\n id: 'zh-Hant',\n label: 'Traditional Chinese',\n lang: 'zh-TW',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-tc', members: [google('noto-sans-tc', 'Noto Sans TC', false)] },\n serif: {\n default: 'noto-serif-tc',\n members: [\n google('noto-serif-tc', 'Noto Serif TC', false),\n // A handwritten-style face, so a pick rather than the default.\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', false),\n ],\n },\n mono: {\n default: 'noto-sans-tc',\n members: [google('noto-sans-tc', 'Noto Sans TC', false, 'sans')],\n },\n },\n },\n // dooooWeb: `japanese`.\n ja: {\n id: 'ja',\n label: 'Japanese',\n lang: 'ja',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-jp',\n members: [\n google('noto-sans-jp', 'Noto Sans JP', true),\n google('m-plus-rounded-1c', 'M PLUS Rounded 1c', true),\n ],\n },\n serif: {\n default: 'noto-serif-jp',\n members: [google('noto-serif-jp', 'Noto Serif JP', false)],\n },\n mono: {\n default: 'noto-sans-jp',\n members: [google('noto-sans-jp', 'Noto Sans JP', false, 'sans')],\n },\n },\n },\n // dooooWeb: `korean`.\n ko: {\n id: 'ko',\n label: 'Korean',\n lang: 'ko',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-kr',\n members: [\n google('noto-sans-kr', 'Noto Sans KR', true),\n google('nanum-gothic', 'Nanum Gothic', true),\n ],\n },\n serif: {\n default: 'noto-serif-kr',\n members: [google('noto-serif-kr', 'Noto Serif KR', false)],\n },\n mono: {\n default: 'noto-sans-kr',\n members: [google('noto-sans-kr', 'Noto Sans KR', false, 'sans')],\n },\n },\n },\n} as const satisfies Record<string, FontSet>;\n\n/** The id of every registered set. */\nexport type FontSetId = keyof typeof FONT_SET_REGISTRY;\n\n/** All set ids, in registry order. */\nexport const FONT_SET_IDS = Object.keys(FONT_SET_REGISTRY) as FontSetId[];\n\n/** The set an unknown or empty page language falls back to: the app renders it in English. */\nexport const DEFAULT_FONT_SET_ID: FontSetId = 'latin';\n\n/** Which set each locale is drawn in (font-sets.md § Locale → set). */\nexport const LOCALE_FONT_SET = {\n en: 'latin',\n zh: 'zh-Hans',\n ja: 'ja',\n ko: 'ko',\n de: 'latin',\n fr: 'latin',\n es: 'latin',\n it: 'latin',\n nl: 'latin',\n pl: 'latin',\n pt: 'latin',\n 'zh-tw': 'zh-Hant',\n} as const satisfies Record<FontSetLanguage, FontSetId>;\n\n/** Narrowing guard: is an arbitrary value one of the eleven locales? */\nexport function isFontSetLocale(value: unknown): value is FontSetLocale {\n return typeof value === 'string' && (FONT_SET_LOCALES as readonly string[]).includes(value);\n}\n\n/** Narrowing guard: is an arbitrary value an app locale or an extra language? */\nexport function isFontSetLanguage(value: unknown): value is FontSetLanguage {\n return (\n isFontSetLocale(value) ||\n (typeof value === 'string' && (FONT_SET_EXTRA_LOCALES as readonly string[]).includes(value))\n );\n}\n\n/**\n * The set for an `<html lang>` value. Tries the whole tag first, case-insensitively\n * and reading `_` as `-` (`zh-TW` / `zh_tw` → zh-Hant), then the primary subtag\n * (`ja-JP` → ja, `PT-br` → pt, `zh-Hans` → zh). No script or region heuristics:\n * `zh-Hant` and `zh-HK` reach zh-Hant only through a page whose `lang` is `zh-TW`.\n *\n * The fallback is explicit: an empty or unknown tag returns the Latin set,\n * because the app renders an unknown language in English. It never falls back\n * to \"the nearest CJK set\". Use `isFontSetLocale` on the primary subtag to tell\n * a fallback from a match.\n */\nexport function resolveFontSet(lang: string): FontSet {\n const tag = lang.trim().toLowerCase().replace(/_/g, '-');\n if (isFontSetLanguage(tag)) return FONT_SET_REGISTRY[LOCALE_FONT_SET[tag]];\n const primary = tag.split('-')[0] ?? '';\n const id = isFontSetLocale(primary) ? LOCALE_FONT_SET[primary] : DEFAULT_FONT_SET_ID;\n return FONT_SET_REGISTRY[id];\n}\n\n/**\n * A member of one role of one set, by id. A missing or unknown id (a stored\n * pick naming a member that has since been removed) returns that role's\n * default, never another set's face.\n */\nexport function resolveFontSetMember(\n setId: FontSetId,\n role: FontSetRole,\n memberId?: string,\n): FontSetMember {\n const r: FontSetRoleMembers = FONT_SET_REGISTRY[setId].roles[role];\n const named = memberId === undefined ? undefined : r.members.find((m) => m.id === memberId);\n return named ?? r.members.find((m) => m.id === r.default)!;\n}\n\n/**\n * The CSS custom property the app's `next/font` loader assigns to a member's\n * face and `theme.css` reads: `--font-set-<set>-<role>-<member>`. This name is\n * the contract between the registry, the loader and the stylesheet, so it is\n * built here and nowhere else.\n *\n * A member that re-uses another role's face (`sameFaceAs`) returns THAT role's\n * variable, so the face is declared once. The `type-pairing` member, and an id\n * the role does not have, return `null`.\n */\nexport function fontSetMemberVar(\n setId: FontSetId,\n role: FontSetRole,\n memberId: string,\n): string | null {\n const member = FONT_SET_REGISTRY[setId].roles[role].members.find(\n (m: FontSetMember) => m.id === memberId,\n );\n if (!member || member.source.kind === 'type-pairing') return null;\n const faceRole = member.sameFaceAs ?? role;\n return `--font-set-${setId}-${faceRole}-${member.id}`;\n}\n\n/** The `<html>` attribute that names a picked member for one role. */\nexport type FontSetPickAttribute = `data-font-set-${FontSetRole}`;\n\n/**\n * The `data-font-set-<role>` attributes a person's pick for `locale` puts on\n * `<html>` (Story MOTIR-7736 · MOTIR-7897). One attribute per role of the\n * locale's set that has a member with this id, unless that member is the\n * `type-pairing` placeholder or the role's default: `theme.css` carries a rule\n * only for a non-default member, so a default needs no attribute.\n *\n * `null`, an unknown id, a default id and every Latin locale return `{}`. This\n * is the ONE derivation the client (`setFontPick`) and the server-rendered\n * first byte share, so a pick sets identical attributes on both sides — which\n * is why it lives here, free of any `'use client'` boundary.\n */\nexport function fontSetPickAttributes(\n locale: FontSetLanguage,\n memberId: string | null,\n): Partial<Record<FontSetPickAttribute, string>> {\n if (memberId === null) return {};\n const set: FontSet = FONT_SET_REGISTRY[LOCALE_FONT_SET[locale]];\n const attrs: Partial<Record<FontSetPickAttribute, string>> = {};\n for (const role of FONT_SET_ROLES) {\n const r: FontSetRoleMembers = set.roles[role];\n if (memberId === r.default) continue;\n const member = r.members.find((m: FontSetMember) => m.id === memberId);\n if (member && member.source.kind !== 'type-pairing') attrs[`data-font-set-${role}`] = memberId;\n }\n return attrs;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motir/design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Motir's 3-axis design system (colour · style · type): distributable token CSS, the axis registries, the theme-apply API, and framework-agnostic React primitives. Shared by motir-core and planner-scaffolded products.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"repository": {
|
package/theme.css
CHANGED
|
@@ -287,6 +287,57 @@
|
|
|
287
287
|
--font-mono-tail: monospace;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
/* The DENSITY STEPS (MOTIR-8901; the vocabulary is MOTIR-2336's approved
|
|
291
|
+
decision, Option 1). A surface's own padding / height is a SCALE, not one
|
|
292
|
+
value, so each step is DERIVED from the base token it scales: the name is
|
|
293
|
+
the step's pixel value in the reference style (warm-editorial, whose bases
|
|
294
|
+
are the @theme ones above), and the value is that base multiplied by
|
|
295
|
+
N / <reference px> and rounded to a whole pixel. Under warm-editorial every
|
|
296
|
+
step is exactly N px; a denser or roomier style moves the base and every step
|
|
297
|
+
follows it, with no per-style declaration to keep in sync.
|
|
298
|
+
|
|
299
|
+
⚠️ TWO SELECTORS, ON PURPOSE. A custom property is substituted where it is
|
|
300
|
+
DECLARED, not where it is used. A rule on `:root` alone would resolve
|
|
301
|
+
`var(--spacing-control-x)` once, against the root's value, and hand that
|
|
302
|
+
frozen length to a nested `[data-style]` subtree (a scoped /tokens tile, a
|
|
303
|
+
style preview) that overrides the base — the same hazard
|
|
304
|
+
`--width-rail-chrome` records above. Listing `[data-style]` beside `:root`
|
|
305
|
+
re-declares every step on each scope that carries its own bases, so it
|
|
306
|
+
re-resolves there.
|
|
307
|
+
|
|
308
|
+
The `[data-style]` blocks are deliberately NOT edited: a step is never
|
|
309
|
+
written per style. A step is added by ONE line here, and only when at least
|
|
310
|
+
three real sites need it. Button, input, icon-button, kbd and tooltip tokens
|
|
311
|
+
carry no steps. */
|
|
312
|
+
:root,
|
|
313
|
+
[data-style] {
|
|
314
|
+
--spacing-card-padding-16: round(nearest, calc(var(--spacing-card-padding) * 16 / 24), 1px);
|
|
315
|
+
--spacing-card-padding-12: round(nearest, calc(var(--spacing-card-padding) * 12 / 24), 1px);
|
|
316
|
+
--spacing-card-padding-8: round(nearest, calc(var(--spacing-card-padding) * 8 / 24), 1px);
|
|
317
|
+
--spacing-card-padding-4: round(nearest, calc(var(--spacing-card-padding) * 4 / 24), 1px);
|
|
318
|
+
|
|
319
|
+
--spacing-control-x-16: round(nearest, calc(var(--spacing-control-x) * 16 / 10), 1px);
|
|
320
|
+
--spacing-control-x-12: round(nearest, calc(var(--spacing-control-x) * 12 / 10), 1px);
|
|
321
|
+
--spacing-control-x-8: round(nearest, calc(var(--spacing-control-x) * 8 / 10), 1px);
|
|
322
|
+
--spacing-control-x-6: round(nearest, calc(var(--spacing-control-x) * 6 / 10), 1px);
|
|
323
|
+
--spacing-control-x-4: round(nearest, calc(var(--spacing-control-x) * 4 / 10), 1px);
|
|
324
|
+
--spacing-control-x-2: round(nearest, calc(var(--spacing-control-x) * 2 / 10), 1px);
|
|
325
|
+
|
|
326
|
+
--spacing-control-y-8: round(nearest, calc(var(--spacing-control-y) * 8 / 6), 1px);
|
|
327
|
+
--spacing-control-y-4: round(nearest, calc(var(--spacing-control-y) * 4 / 6), 1px);
|
|
328
|
+
--spacing-control-y-2: round(nearest, calc(var(--spacing-control-y) * 2 / 6), 1px);
|
|
329
|
+
|
|
330
|
+
--spacing-chip-x-8: round(nearest, calc(var(--spacing-chip-x) * 8 / 10), 1px);
|
|
331
|
+
--spacing-chip-x-6: round(nearest, calc(var(--spacing-chip-x) * 6 / 10), 1px);
|
|
332
|
+
--spacing-chip-x-4: round(nearest, calc(var(--spacing-chip-x) * 4 / 10), 1px);
|
|
333
|
+
--spacing-chip-x-2: round(nearest, calc(var(--spacing-chip-x) * 2 / 10), 1px);
|
|
334
|
+
|
|
335
|
+
--height-control-28: round(nearest, calc(var(--height-control) * 28 / 36), 1px);
|
|
336
|
+
--height-control-32: round(nearest, calc(var(--height-control) * 32 / 36), 1px);
|
|
337
|
+
--height-control-40: round(nearest, calc(var(--height-control) * 40 / 36), 1px);
|
|
338
|
+
--height-control-48: round(nearest, calc(var(--height-control) * 48 / 36), 1px);
|
|
339
|
+
}
|
|
340
|
+
|
|
290
341
|
/* ═══════════════════════════════════════════════════════════════════════
|
|
291
342
|
TIER 1 — Dark mode color overrides
|
|
292
343
|
Activated when [data-theme="dark"] is set on <html> (by the init
|
|
@@ -3856,6 +3907,10 @@
|
|
|
3856
3907
|
--el-chart-surplus: var(--color-success); /* fill where resolved > created */
|
|
3857
3908
|
--el-chart-age: var(--color-warning); /* average-age bar (8.8.13 — aging backlog) */
|
|
3858
3909
|
--el-chart-resolution: var(--color-info); /* resolution-time bar (8.8.13 — throughput) */
|
|
3910
|
+
--el-chart-workload-assigned: var(--color-primary); /* workload: assigned segment (MOTIR-8925) */
|
|
3911
|
+
--el-chart-workload-unassigned: var(
|
|
3912
|
+
--color-accent-teal
|
|
3913
|
+
); /* workload: unassigned-by-reporter segment */
|
|
3859
3914
|
/* Sprint CYCLE GRAPH series (Story 8.14 — Linear scope-vs-completed). A
|
|
3860
3915
|
collision-free --el-chart-cycle-* family: the bare --el-chart-completed /
|
|
3861
3916
|
-started / -target names are taken (velocity / old burndown), so reusing
|
|
@@ -4045,6 +4100,16 @@
|
|
|
4045
4100
|
--el-showcase-wave-1: var(--color-primary);
|
|
4046
4101
|
--el-showcase-wave-2: var(--color-accent-teal);
|
|
4047
4102
|
--el-showcase-wave-3: var(--color-accent);
|
|
4103
|
+
/* The Motir mark AT WORK (MOTIR-9212 · MOTIR-9213): the three hues the mark
|
|
4104
|
+
cycles through while it flows on the ink circle (`--el-accent`). Each wave
|
|
4105
|
+
hue pulled toward the ink already on the circle, the shape of
|
|
4106
|
+
--el-danger-on-surface: the raw hues fall to 1.00:1 on the circle in the
|
|
4107
|
+
palettes whose accent IS a wave hue. At 35% every palette and theme keeps
|
|
4108
|
+
3:1 (WCAG 1.4.11, a graphic) for each role and every blend between them;
|
|
4109
|
+
the default Motir palette keeps 60% (its own blocks, below). */
|
|
4110
|
+
--el-mark-wave-1: color-mix(in srgb, var(--el-showcase-wave-1) 35%, var(--el-accent-text));
|
|
4111
|
+
--el-mark-wave-2: color-mix(in srgb, var(--el-showcase-wave-2) 35%, var(--el-accent-text));
|
|
4112
|
+
--el-mark-wave-3: color-mix(in srgb, var(--el-showcase-wave-3) 35%, var(--el-accent-text));
|
|
4048
4113
|
/* motir.co's live link ("Motir builds itself" in the header): its square
|
|
4049
4114
|
and its words walk the wave hues. The mark is each hue as is; the text
|
|
4050
4115
|
is each hue mixed 60/40 with the primary ink, because the raw teal and
|
|
@@ -4340,6 +4405,9 @@
|
|
|
4340
4405
|
palettes override, restated so a scoped amethyst tile keeps the base. */
|
|
4341
4406
|
--el-logo-mark: var(--color-primary-fill);
|
|
4342
4407
|
--el-logo-tile: color-mix(in srgb, var(--color-primary-fill) 14%, var(--color-background));
|
|
4408
|
+
--el-mark-wave-1: color-mix(in srgb, var(--el-showcase-wave-1) 35%, var(--el-accent-text));
|
|
4409
|
+
--el-mark-wave-2: color-mix(in srgb, var(--el-showcase-wave-2) 35%, var(--el-accent-text));
|
|
4410
|
+
--el-mark-wave-3: color-mix(in srgb, var(--el-showcase-wave-3) 35%, var(--el-accent-text));
|
|
4343
4411
|
--el-showcase-highlight: color-mix(in srgb, var(--color-accent) 55%, var(--color-background));
|
|
4344
4412
|
--el-showcase-decision: color-mix(in srgb, var(--color-accent) 70%, var(--color-background));
|
|
4345
4413
|
}
|
|
@@ -4603,6 +4671,11 @@
|
|
|
4603
4671
|
ink CTA, not its identity hue) on a 28% wash of that blue. */
|
|
4604
4672
|
--el-logo-mark: var(--color-primary);
|
|
4605
4673
|
--el-logo-tile: color-mix(in srgb, var(--color-primary) 28%, var(--color-background));
|
|
4674
|
+
/* The mark at work keeps more of its hue on the Motir circle (MOTIR-9212):
|
|
4675
|
+
its lowest pair is 4.40:1 at 60%, and the hues stay the prototype's. */
|
|
4676
|
+
--el-mark-wave-1: color-mix(in srgb, var(--el-showcase-wave-1) 60%, var(--el-accent-text));
|
|
4677
|
+
--el-mark-wave-2: color-mix(in srgb, var(--el-showcase-wave-2) 60%, var(--el-accent-text));
|
|
4678
|
+
--el-mark-wave-3: color-mix(in srgb, var(--el-showcase-wave-3) 60%, var(--el-accent-text));
|
|
4606
4679
|
/* brand & primary — the high-contrast INK CTA + the single cool-blue accent.
|
|
4607
4680
|
--color-primary is the accent AS text/icon/focus on a surface (the cool
|
|
4608
4681
|
blue, AA on white & the slate surface); --color-primary-fill is the CTA
|
|
@@ -4740,6 +4813,9 @@
|
|
|
4740
4813
|
[data-palette='motir'][data-theme='dark'] {
|
|
4741
4814
|
--el-logo-mark: var(--color-primary); /* parity with the light block */
|
|
4742
4815
|
--el-logo-tile: color-mix(in srgb, var(--color-primary) 28%, var(--color-background));
|
|
4816
|
+
--el-mark-wave-1: color-mix(in srgb, var(--el-showcase-wave-1) 60%, var(--el-accent-text));
|
|
4817
|
+
--el-mark-wave-2: color-mix(in srgb, var(--el-showcase-wave-2) 60%, var(--el-accent-text));
|
|
4818
|
+
--el-mark-wave-3: color-mix(in srgb, var(--el-showcase-wave-3) 60%, var(--el-accent-text));
|
|
4743
4819
|
--color-background: #0c0d0f; /* stark cool near-black canvas */
|
|
4744
4820
|
--color-foreground: #edeef0;
|
|
4745
4821
|
--color-surface: #18191c; /* cool slate section */
|
|
@@ -6224,6 +6300,23 @@
|
|
|
6224
6300
|
--font-script-serif: var(--font-set-zh-Hans-serif-lxgw-wenkai-tc, serif);
|
|
6225
6301
|
}
|
|
6226
6302
|
|
|
6303
|
+
/* Traditional Chinese, Taiwan (zh-Hant · MOTIR-8937): each role's default
|
|
6304
|
+
member. `:lang(zh)` also matches zh-TW, so these blocks come AFTER both zh
|
|
6305
|
+
blocks above: the specificity is equal and source order hands zh-TW its own
|
|
6306
|
+
faces, including over a zh by-name pick of the same attribute. */
|
|
6307
|
+
[lang]:lang(zh-TW) {
|
|
6308
|
+
--font-script-sans: var(--font-set-zh-Hant-sans-noto-sans-tc, sans-serif);
|
|
6309
|
+
--font-script-serif: var(--font-set-zh-Hant-serif-noto-serif-tc, serif);
|
|
6310
|
+
--font-script-mono: var(--font-set-zh-Hant-sans-noto-sans-tc, monospace);
|
|
6311
|
+
}
|
|
6312
|
+
|
|
6313
|
+
/* A member picked by name. Comes after the set's block, so on an element
|
|
6314
|
+
carrying both a lang and the attribute this one wins the specificity tie.
|
|
6315
|
+
An id the registry does not list matches nothing and keeps the default. */
|
|
6316
|
+
:lang(zh-TW)[data-font-set-serif='lxgw-wenkai-tc'] {
|
|
6317
|
+
--font-script-serif: var(--font-set-zh-Hant-serif-lxgw-wenkai-tc, serif);
|
|
6318
|
+
}
|
|
6319
|
+
|
|
6227
6320
|
/* Japanese (ja): each role's default member. A CJK mono role
|
|
6228
6321
|
draws its Han glyphs from the set's sans face (fontSets.ts sameFaceAs), so
|
|
6229
6322
|
the block stays total over the three roles while loading no extra face. */
|