@konce-pt/theme 0.9.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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/audit.d.ts +59 -0
- package/dist/audit.js +102 -0
- package/dist/dtcg.d.ts +80 -0
- package/dist/dtcg.js +624 -0
- package/dist/emit.d.ts +76 -0
- package/dist/emit.js +276 -0
- package/dist/generated/tokens.d.ts +24 -0
- package/dist/generated/tokens.js +348 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/ramp.d.ts +41 -0
- package/dist/ramp.js +66 -0
- package/dist/resolve.d.ts +56 -0
- package/dist/resolve.js +192 -0
- package/dist/roles.d.ts +28 -0
- package/dist/roles.js +77 -0
- package/dist/runtime.d.ts +32 -0
- package/dist/runtime.js +41 -0
- package/dist/schema.d.ts +128 -0
- package/dist/schema.js +129 -0
- package/package.json +52 -0
package/dist/emit.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emitery motywu. Każdy bierze dokument i zwraca tekst pliku — nic nie zapisuje i nic nie wie
|
|
3
|
+
* o systemie plików, bo edytor działa w przeglądarce.
|
|
4
|
+
*
|
|
5
|
+
* Jedna zasada wiąże je wszystkie z podglądem: podgląd wstrzykuje **dokładnie ten tekst**, który
|
|
6
|
+
* zwraca `emitCss`. Nie „równoważny" — ten sam. Narzędzie, którego podgląd rozjeżdża się
|
|
7
|
+
* z artefaktem, nie ma racji bytu.
|
|
8
|
+
*/
|
|
9
|
+
import type { KptTokenMap } from './resolve.ts';
|
|
10
|
+
import type { KptThemeDoc, KptThemeMode } from './schema.ts';
|
|
11
|
+
export interface KptEmitOptions {
|
|
12
|
+
/** Selektor motywu ciemnego. Domyślnie ten, którego używa biblioteka. */
|
|
13
|
+
darkSelector?: string;
|
|
14
|
+
/** Dołóż blok `@media (prefers-color-scheme: dark)` — biblioteka sama go nie ma. */
|
|
15
|
+
prefersColorScheme?: boolean;
|
|
16
|
+
/** Patrz `KptThemeExport.compat`. */
|
|
17
|
+
compat?: 'resolved' | 'layered';
|
|
18
|
+
/** Wyemituj komplet tokenów zamiast różnicy. Do podglądu całości i do diagnostyki. */
|
|
19
|
+
complete?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Co trafia do pliku, dla jednego motywu.
|
|
23
|
+
*
|
|
24
|
+
* `resolved` (domyślne) emituje **całą różnicę wartościami dosłownymi**, prymitywy w to wliczając,
|
|
25
|
+
* i powtarza je w obu blokach. Powtarzanie nie jest marnotrawstwem: w wersjach `@konce-pt/tokens`
|
|
26
|
+
* sprzed poprawki `tokens.dark.css` deklarował komplet prymitywów wewnątrz `[data-theme="dark"]`,
|
|
27
|
+
* więc nadpisanie prymitywu w samym `:root` działało w motywie jasnym, a w ciemnym było po cichu
|
|
28
|
+
* przebijane. Blok ciemny idący później wygrywa z nim kolejnością i plik zachowuje się tak samo
|
|
29
|
+
* na każdej wersji biblioteki.
|
|
30
|
+
*
|
|
31
|
+
* Prymitywów **nie wolno pomijać**, choć kusi to przy kolorach: warstwa semantyczna niesie cały
|
|
32
|
+
* kolor, ale nie niesie typografii ani odstępów. Żaden token nie powołuje się na `--kpt-font-size-*`
|
|
33
|
+
* — komponenty czytają je wprost w swoich regułach — więc plik bez prymitywów gubiłby zmianę kroju
|
|
34
|
+
* i skali pisma w całości. Przy promieniach szkoda byłaby częściowa i przez to gorsza: sześć
|
|
35
|
+
* tokenów komponentowych powołuje się na `--kpt-radius-*`, więc część rogów by się zmieniła,
|
|
36
|
+
* a część nie.
|
|
37
|
+
*
|
|
38
|
+
* `layered` emituje prymitywy raz, w `:root`, i tylko te tokeny, których wartości **nie** tłumaczy
|
|
39
|
+
* już kaskada po nich — plik krótszy i czytelniejszy, ale wymagający wersji biblioteki z poprawką
|
|
40
|
+
* ciemnego arkusza.
|
|
41
|
+
*/
|
|
42
|
+
export declare function emitPlan(doc: KptThemeDoc, mode: KptThemeMode, options?: KptEmitOptions): KptTokenMap;
|
|
43
|
+
/**
|
|
44
|
+
* Arkusz motywu. Blok ciemny emitujemy **zawsze**, nawet identyczny z jasnym: bez niego
|
|
45
|
+
* `data-theme="dark"` po cichu wraca do wartości stockowych, a to jest najtrudniejszy do
|
|
46
|
+
* znalezienia rodzaj usterki motywu.
|
|
47
|
+
*/
|
|
48
|
+
export declare function emitCss(doc: KptThemeDoc, options?: KptEmitOptions): string;
|
|
49
|
+
/** Ten sam motyw jako mixiny SCSS — dla konsumentów kompilujących style ze źródeł. */
|
|
50
|
+
export declare function emitScss(doc: KptThemeDoc, options?: KptEmitOptions): string;
|
|
51
|
+
/**
|
|
52
|
+
* Motyw jako moduł TypeScriptu, razem z funkcją nakładającą go w runtime.
|
|
53
|
+
*
|
|
54
|
+
* Plik jest samowystarczalny — zero importów — bo trafia do repozytorium konsumenta, gdzie
|
|
55
|
+
* `@konce-pt/theme` nie musi być zainstalowany. To jest droga dla aplikacji, w których motyw
|
|
56
|
+
* wybiera użytkownik końcowy, a nie build.
|
|
57
|
+
*/
|
|
58
|
+
export declare function emitTs(doc: KptThemeDoc, options?: KptEmitOptions): string;
|
|
59
|
+
/**
|
|
60
|
+
* Eksport w formacie W3C Design Tokens. Tryby jasny i ciemny idą jako dwie grupy najwyższego
|
|
61
|
+
* poziomu, bo **DTCG nie standaryzuje trybów** — każdy eksporter koduje je inaczej i nie ma
|
|
62
|
+
* zapisu, który byłby tu „poprawny". Dwie grupy są przynajmniej jednoznaczne i czytelne dla
|
|
63
|
+
* człowieka, a importer po drugiej stronie i tak wymaga wskazania, co jest czym.
|
|
64
|
+
*
|
|
65
|
+
* To jest eksport do narzędzi projektowych, nie synchronizacja.
|
|
66
|
+
*/
|
|
67
|
+
export declare function emitDtcg(doc: KptThemeDoc, options?: KptEmitOptions): string;
|
|
68
|
+
/** Ile tokenów zmienia motyw względem stocku — do podsumowania w interfejsie. */
|
|
69
|
+
export declare function themeSize(doc: KptThemeDoc): {
|
|
70
|
+
light: number;
|
|
71
|
+
dark: number;
|
|
72
|
+
semantic: number;
|
|
73
|
+
};
|
|
74
|
+
/** Wartości stockowe — wystawione, żeby edytor mógł pokazać „wróć do domyślnej". */
|
|
75
|
+
export declare const stockValue: (name: string, mode: KptThemeMode) => string | undefined;
|
|
76
|
+
export declare const rawValue: (name: string, mode: KptThemeMode) => string | undefined;
|
package/dist/emit.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emitery motywu. Każdy bierze dokument i zwraca tekst pliku — nic nie zapisuje i nic nie wie
|
|
3
|
+
* o systemie plików, bo edytor działa w przeglądarce.
|
|
4
|
+
*
|
|
5
|
+
* Jedna zasada wiąże je wszystkie z podglądem: podgląd wstrzykuje **dokładnie ten tekst**, który
|
|
6
|
+
* zwraca `emitCss`. Nie „równoważny" — ten sam. Narzędzie, którego podgląd rozjeżdża się
|
|
7
|
+
* z artefaktem, nie ma racji bytu.
|
|
8
|
+
*/
|
|
9
|
+
import { isKnownToken, isPrimitiveToken, resolveTheme, stockTheme, themeDiff } from "./resolve.js";
|
|
10
|
+
import { KPT_RAW, KPT_SEMANTIC_TOKENS } from "./generated/tokens.js";
|
|
11
|
+
const DEFAULT_DARK_SELECTOR = '[data-theme="dark"]';
|
|
12
|
+
const SEMANTIC = new Set(KPT_SEMANTIC_TOKENS);
|
|
13
|
+
const optionsOf = (doc, options = {}) => ({
|
|
14
|
+
darkSelector: options.darkSelector ?? doc.export?.darkSelector ?? DEFAULT_DARK_SELECTOR,
|
|
15
|
+
prefersColorScheme: options.prefersColorScheme ?? doc.export?.prefersColorScheme ?? false,
|
|
16
|
+
compat: options.compat ?? doc.export?.compat ?? 'resolved',
|
|
17
|
+
complete: options.complete ?? false,
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Co trafia do pliku, dla jednego motywu.
|
|
21
|
+
*
|
|
22
|
+
* `resolved` (domyślne) emituje **całą różnicę wartościami dosłownymi**, prymitywy w to wliczając,
|
|
23
|
+
* i powtarza je w obu blokach. Powtarzanie nie jest marnotrawstwem: w wersjach `@konce-pt/tokens`
|
|
24
|
+
* sprzed poprawki `tokens.dark.css` deklarował komplet prymitywów wewnątrz `[data-theme="dark"]`,
|
|
25
|
+
* więc nadpisanie prymitywu w samym `:root` działało w motywie jasnym, a w ciemnym było po cichu
|
|
26
|
+
* przebijane. Blok ciemny idący później wygrywa z nim kolejnością i plik zachowuje się tak samo
|
|
27
|
+
* na każdej wersji biblioteki.
|
|
28
|
+
*
|
|
29
|
+
* Prymitywów **nie wolno pomijać**, choć kusi to przy kolorach: warstwa semantyczna niesie cały
|
|
30
|
+
* kolor, ale nie niesie typografii ani odstępów. Żaden token nie powołuje się na `--kpt-font-size-*`
|
|
31
|
+
* — komponenty czytają je wprost w swoich regułach — więc plik bez prymitywów gubiłby zmianę kroju
|
|
32
|
+
* i skali pisma w całości. Przy promieniach szkoda byłaby częściowa i przez to gorsza: sześć
|
|
33
|
+
* tokenów komponentowych powołuje się na `--kpt-radius-*`, więc część rogów by się zmieniła,
|
|
34
|
+
* a część nie.
|
|
35
|
+
*
|
|
36
|
+
* `layered` emituje prymitywy raz, w `:root`, i tylko te tokeny, których wartości **nie** tłumaczy
|
|
37
|
+
* już kaskada po nich — plik krótszy i czytelniejszy, ale wymagający wersji biblioteki z poprawką
|
|
38
|
+
* ciemnego arkusza.
|
|
39
|
+
*/
|
|
40
|
+
export function emitPlan(doc, mode, options = {}) {
|
|
41
|
+
return plansOf(doc, options)[mode];
|
|
42
|
+
}
|
|
43
|
+
/** Para planów dla obu motywów. Liczona raz, bo `balance` i tak potrzebuje obu naraz. */
|
|
44
|
+
function plansOf(doc, options = {}) {
|
|
45
|
+
if (optionsOf(doc, options).complete) {
|
|
46
|
+
const all = resolveTheme(doc);
|
|
47
|
+
return { light: { ...all.light }, dark: { ...all.dark } };
|
|
48
|
+
}
|
|
49
|
+
return balance(doc, options);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Oba bloki z **tym samym zestawem kluczy**. To nie jest ozdoba, tylko warunek poprawności.
|
|
53
|
+
*
|
|
54
|
+
* Plik motywu ładuje się PO `tokens.dark.css`, a `:root` i `[data-theme="dark"]` mają dokładnie
|
|
55
|
+
* tę samą specyficzność — przy remisie rozstrzyga kolejność w dokumencie. Nasz `:root` stoi więc
|
|
56
|
+
* wyżej niż biblioteczne nadpisanie ciemne. Token zmieniony tylko w motywie jasnym i wypisany
|
|
57
|
+
* w samym `:root` **przecieknąłby do ciemnego**: tam nikt by go już nie przykrył.
|
|
58
|
+
*
|
|
59
|
+
* Dlatego dla każdego tokenu, który trafia do jednego bloku, drugi blok dostaje jego własną
|
|
60
|
+
* wartość — najczęściej stockową, czyli dosłownie przywrócenie tego, co biblioteka i tak miała.
|
|
61
|
+
* Wygląda na nadmiarowe i takie jest w połowie przypadków; druga połowa to różnica między motywem
|
|
62
|
+
* działającym a ciemnym, który po cichu przejmuje barwy jasnego.
|
|
63
|
+
*/
|
|
64
|
+
function balance(doc, options = {}) {
|
|
65
|
+
const plans = {
|
|
66
|
+
light: planFor(doc, 'light', options),
|
|
67
|
+
dark: planFor(doc, 'dark', options),
|
|
68
|
+
};
|
|
69
|
+
const all = new Set([...Object.keys(plans.light), ...Object.keys(plans.dark)]);
|
|
70
|
+
const full = resolveTheme(doc);
|
|
71
|
+
for (const mode of ['light', 'dark']) {
|
|
72
|
+
for (const name of all) {
|
|
73
|
+
plans[mode][name] ??= full[mode][name] ?? '';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return plans;
|
|
77
|
+
}
|
|
78
|
+
function planFor(doc, mode, options = {}) {
|
|
79
|
+
const resolved = optionsOf(doc, options);
|
|
80
|
+
const diff = themeDiff(doc, mode);
|
|
81
|
+
if (resolved.compat === 'resolved')
|
|
82
|
+
return diff;
|
|
83
|
+
// Warstwowo: najpierw same prymitywy, potem to, czego one nie wyjaśniają.
|
|
84
|
+
const primitiveOnly = { kptTheme: doc.kptTheme, overrides: { primitive: {} } };
|
|
85
|
+
for (const [name, value] of Object.entries(diff)) {
|
|
86
|
+
if (isPrimitiveToken(name))
|
|
87
|
+
primitiveOnly.overrides.primitive[name] = value;
|
|
88
|
+
}
|
|
89
|
+
const afterPrimitives = resolveTheme(primitiveOnly)[mode];
|
|
90
|
+
const plan = { ...primitiveOnly.overrides.primitive };
|
|
91
|
+
for (const [name, value] of Object.entries(diff)) {
|
|
92
|
+
if (!isPrimitiveToken(name) && afterPrimitives[name] !== value)
|
|
93
|
+
plan[name] = value;
|
|
94
|
+
}
|
|
95
|
+
return plan;
|
|
96
|
+
}
|
|
97
|
+
const sorted = (map) => Object.entries(map).sort(([a], [b]) => a.localeCompare(b));
|
|
98
|
+
const block = (selector, map, indent = ' ') => {
|
|
99
|
+
const body = sorted(map)
|
|
100
|
+
.map(([name, value]) => `${indent}${name}: ${value};`)
|
|
101
|
+
.join('\n');
|
|
102
|
+
return `${selector} {\n${body}\n}`;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Nagłówek arkusza — razem z instrukcją ładowania.
|
|
106
|
+
*
|
|
107
|
+
* Instrukcja jedzie w komentarzu, a nie w osobnym pliku, bo osobny plik ginie przy pierwszym
|
|
108
|
+
* skopiowaniu samego arkusza — a arkusz kopiuje się częściej, niż pobiera całą paczkę. Kolejność
|
|
109
|
+
* jest tu jedyną rzeczą, którą da się zrobić źle bez żadnego komunikatu błędu: plik wczytany przed
|
|
110
|
+
* `tokens.css` po prostu nic nie robi.
|
|
111
|
+
*/
|
|
112
|
+
const header = (doc) => {
|
|
113
|
+
const name = doc.meta?.name ? `"${doc.meta.name}" ` : '';
|
|
114
|
+
const based = doc.meta?.basedOn ? ` · ${doc.meta.basedOn.package} ${doc.meta.basedOn.version}` : '';
|
|
115
|
+
const stamp = doc.meta?.updatedAt ? ` · ${doc.meta.updatedAt}` : '';
|
|
116
|
+
return [
|
|
117
|
+
`/* Koncept UI theme ${name}— ui.konce.pt/theme`,
|
|
118
|
+
` * kpt-theme ${doc.kptTheme}${based}${stamp}`,
|
|
119
|
+
' *',
|
|
120
|
+
' * Load AFTER @konce-pt/tokens/css and @konce-pt/tokens/css/dark.',
|
|
121
|
+
' *',
|
|
122
|
+
' * Angular CLI — angular.json > styles[], in this order:',
|
|
123
|
+
' * node_modules/@konce-pt/tokens/dist/css/tokens.css',
|
|
124
|
+
' * node_modules/@konce-pt/tokens/dist/css/tokens.dark.css',
|
|
125
|
+
' * node_modules/@konce-pt/styles/index.css',
|
|
126
|
+
' * src/kpt-theme.css <- this file',
|
|
127
|
+
' * src/styles.scss',
|
|
128
|
+
" * Do NOT import it from main.ts — under the CLI that yields an orphaned chunk and no styles.",
|
|
129
|
+
' *',
|
|
130
|
+
' * Vite / React:',
|
|
131
|
+
" * import '@konce-pt/tokens/css';",
|
|
132
|
+
" * import '@konce-pt/tokens/css/dark';",
|
|
133
|
+
" * import '@konce-pt/styles';",
|
|
134
|
+
" * import '@konce-pt/styles/components'; // React only — this is what styles kpt-*",
|
|
135
|
+
" * import './kpt-theme.css'; // last one wins",
|
|
136
|
+
' *',
|
|
137
|
+
' * Dark mode is an attribute on the root element and the library does not read',
|
|
138
|
+
' * prefers-color-scheme — the application sets it:',
|
|
139
|
+
' * document.documentElement.dataset.theme =',
|
|
140
|
+
" * matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';",
|
|
141
|
+
' */',
|
|
142
|
+
].join('\n');
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Arkusz motywu. Blok ciemny emitujemy **zawsze**, nawet identyczny z jasnym: bez niego
|
|
146
|
+
* `data-theme="dark"` po cichu wraca do wartości stockowych, a to jest najtrudniejszy do
|
|
147
|
+
* znalezienia rodzaj usterki motywu.
|
|
148
|
+
*/
|
|
149
|
+
export function emitCss(doc, options = {}) {
|
|
150
|
+
const resolved = optionsOf(doc, options);
|
|
151
|
+
const { light, dark } = plansOf(doc, options);
|
|
152
|
+
const parts = [header(doc), '', block(':root', light), '', block(resolved.darkSelector, dark)];
|
|
153
|
+
if (resolved.prefersColorScheme) {
|
|
154
|
+
// Biblioteka nie czyta `prefers-color-scheme` — aplikacja albo ustawia `data-theme` sama,
|
|
155
|
+
// albo bierze ten blok. Negacja na `light` zostawia jawny wybór użytkownika ważniejszym
|
|
156
|
+
// od ustawienia systemu.
|
|
157
|
+
const inner = block(':root:not([data-theme="light"])', dark, ' ')
|
|
158
|
+
.split('\n')
|
|
159
|
+
.map((line) => ` ${line}`)
|
|
160
|
+
.join('\n');
|
|
161
|
+
parts.push('', '@media (prefers-color-scheme: dark) {', inner, '}');
|
|
162
|
+
}
|
|
163
|
+
return `${parts.join('\n')}\n`;
|
|
164
|
+
}
|
|
165
|
+
/** Ten sam motyw jako mixiny SCSS — dla konsumentów kompilujących style ze źródeł. */
|
|
166
|
+
export function emitScss(doc, options = {}) {
|
|
167
|
+
const resolved = optionsOf(doc, options);
|
|
168
|
+
const plans = plansOf(doc, options);
|
|
169
|
+
const declare = (map) => sorted(map)
|
|
170
|
+
.map(([name, value]) => ` ${name}: ${value};`)
|
|
171
|
+
.join('\n');
|
|
172
|
+
return `// Koncept UI theme${doc.meta?.name ? ` "${doc.meta.name}"` : ''} — ui.konce.pt/theme
|
|
173
|
+
// Użycie: @use './kpt-theme'; @include kpt-theme.light; @include kpt-theme.dark;
|
|
174
|
+
|
|
175
|
+
@mixin light {
|
|
176
|
+
:root {
|
|
177
|
+
${declare(plans.light)}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@mixin dark {
|
|
182
|
+
${resolved.darkSelector} {
|
|
183
|
+
${declare(plans.dark)}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
`;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Motyw jako moduł TypeScriptu, razem z funkcją nakładającą go w runtime.
|
|
190
|
+
*
|
|
191
|
+
* Plik jest samowystarczalny — zero importów — bo trafia do repozytorium konsumenta, gdzie
|
|
192
|
+
* `@konce-pt/theme` nie musi być zainstalowany. To jest droga dla aplikacji, w których motyw
|
|
193
|
+
* wybiera użytkownik końcowy, a nie build.
|
|
194
|
+
*/
|
|
195
|
+
export function emitTs(doc, options = {}) {
|
|
196
|
+
const plans = plansOf(doc, options);
|
|
197
|
+
const entries = (mode) => sorted(plans[mode])
|
|
198
|
+
.map(([name, value]) => ` ${JSON.stringify(name)}: ${JSON.stringify(value)},`)
|
|
199
|
+
.join('\n');
|
|
200
|
+
return `// Koncept UI theme${doc.meta?.name ? ` "${doc.meta.name}"` : ''} — ui.konce.pt/theme
|
|
201
|
+
// Wygenerowane. Zero zależności — plik jest samowystarczalny.
|
|
202
|
+
|
|
203
|
+
export const KPT_THEME = {
|
|
204
|
+
light: {
|
|
205
|
+
${entries('light')}
|
|
206
|
+
},
|
|
207
|
+
dark: {
|
|
208
|
+
${entries('dark')}
|
|
209
|
+
},
|
|
210
|
+
} as const;
|
|
211
|
+
|
|
212
|
+
export type KptThemeMode = keyof typeof KPT_THEME;
|
|
213
|
+
|
|
214
|
+
/** Nakłada motyw na element (domyślnie <html>) przez style inline. Zwraca funkcję cofającą. */
|
|
215
|
+
export function applyKptTheme(mode: KptThemeMode, element: HTMLElement = document.documentElement): () => void {
|
|
216
|
+
const applied = Object.entries(KPT_THEME[mode]);
|
|
217
|
+
for (const [name, value] of applied) element.style.setProperty(name, value);
|
|
218
|
+
return () => {
|
|
219
|
+
for (const [name] of applied) element.style.removeProperty(name);
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
const COLOR = /^(oklch|rgb|hsl|color|#)/i;
|
|
225
|
+
const DIMENSION = /^-?(?:\d*\.\d+|\d+)(px|rem|em|%)$/;
|
|
226
|
+
/** Typ DTCG zgadnięty z kształtu wartości — źródła biblioteki nie niosą typów w tej postaci. */
|
|
227
|
+
const dtcgType = (value) => {
|
|
228
|
+
if (COLOR.test(value.trim()))
|
|
229
|
+
return 'color';
|
|
230
|
+
if (DIMENSION.test(value.trim()))
|
|
231
|
+
return 'dimension';
|
|
232
|
+
if (/^-?(?:\d*\.\d+|\d+)$/.test(value.trim()))
|
|
233
|
+
return 'number';
|
|
234
|
+
if (value.includes('px ') || value.includes('rem '))
|
|
235
|
+
return 'shadow';
|
|
236
|
+
return 'other';
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Eksport w formacie W3C Design Tokens. Tryby jasny i ciemny idą jako dwie grupy najwyższego
|
|
240
|
+
* poziomu, bo **DTCG nie standaryzuje trybów** — każdy eksporter koduje je inaczej i nie ma
|
|
241
|
+
* zapisu, który byłby tu „poprawny". Dwie grupy są przynajmniej jednoznaczne i czytelne dla
|
|
242
|
+
* człowieka, a importer po drugiej stronie i tak wymaga wskazania, co jest czym.
|
|
243
|
+
*
|
|
244
|
+
* To jest eksport do narzędzi projektowych, nie synchronizacja.
|
|
245
|
+
*/
|
|
246
|
+
export function emitDtcg(doc, options = {}) {
|
|
247
|
+
const plans = plansOf(doc, options);
|
|
248
|
+
const group = (mode) => {
|
|
249
|
+
const out = {};
|
|
250
|
+
for (const [name, value] of sorted(plans[mode])) {
|
|
251
|
+
// Prefiks zdejmujemy także tokenom własnym: w DTCG nazwa jest ścieżką w drzewie, a nie
|
|
252
|
+
// custom property, więc `--` po drugiej stronie jest śmieciem, nie informacją.
|
|
253
|
+
out[name.replace(/^--(kpt-)?/, '')] = {
|
|
254
|
+
$value: value,
|
|
255
|
+
$type: dtcgType(value),
|
|
256
|
+
...(isKnownToken(name) ? {} : { $description: 'Token własny motywu (spoza biblioteki).' }),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
};
|
|
261
|
+
return `${JSON.stringify({
|
|
262
|
+
$description: `Koncept UI theme${doc.meta?.name ? ` "${doc.meta.name}"` : ''} — export, not sync.`,
|
|
263
|
+
light: group('light'),
|
|
264
|
+
dark: group('dark'),
|
|
265
|
+
}, null, 2)}\n`;
|
|
266
|
+
}
|
|
267
|
+
/** Ile tokenów zmienia motyw względem stocku — do podsumowania w interfejsie. */
|
|
268
|
+
export function themeSize(doc) {
|
|
269
|
+
const light = themeDiff(doc, 'light');
|
|
270
|
+
const dark = themeDiff(doc, 'dark');
|
|
271
|
+
const semantic = new Set([...Object.keys(light), ...Object.keys(dark)].filter((n) => SEMANTIC.has(n)));
|
|
272
|
+
return { light: Object.keys(light).length, dark: Object.keys(dark).length, semantic: semantic.size };
|
|
273
|
+
}
|
|
274
|
+
/** Wartości stockowe — wystawione, żeby edytor mógł pokazać „wróć do domyślnej". */
|
|
275
|
+
export const stockValue = (name, mode) => stockTheme()[mode][name];
|
|
276
|
+
export const rawValue = (name, mode) => KPT_RAW[name]?.[mode];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Stopnie pełnej skali, tak jak rozkłada je rampa neutralna biblioteki. */
|
|
2
|
+
export declare const KPT_RAMP_STEPS: readonly number[];
|
|
3
|
+
/** Jasność OKLCH na każdym stopniu — krzywa wzięta z rampy neutralnej. */
|
|
4
|
+
export declare const KPT_RAMP_LIGHTNESS: Readonly<Record<number, number>>;
|
|
5
|
+
/** Kształt nasycenia na każdym stopniu, 0–1: uśredniony z czterech ramp akcentowych. */
|
|
6
|
+
export declare const KPT_RAMP_CHROMA: Readonly<Record<number, number>>;
|
|
7
|
+
/** Największe nasycenie w rampach akcentowych — skala, do której odnosi się krzywa. */
|
|
8
|
+
export declare const KPT_RAMP_PEAK_CHROMA = 0.18325;
|
|
9
|
+
/** Stopnie rodziny akcentowej per motyw, odczytane z danger/success/warning/info. */
|
|
10
|
+
export declare const KPT_ACCENT_PATTERN: Readonly<Record<string, {
|
|
11
|
+
light: number;
|
|
12
|
+
dark: number;
|
|
13
|
+
}>>;
|
|
14
|
+
/** Nazwy tokenów warstwy semantycznej (te i tylko te zmienia motyw). */
|
|
15
|
+
export declare const KPT_SEMANTIC_TOKENS: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* Wartości stockowe wszystkich tokenów w obu motywach, w postaci SUROWEJ: `{--kpt-nazwa}` to
|
|
18
|
+
* referencja do innego tokenu. Rozwiązuje je `resolveTheme` — dzięki temu nadpisanie prymitywu
|
|
19
|
+
* przestawia wszystko, co się na niego powołuje, tak samo jak zrobiłaby to kaskada CSS.
|
|
20
|
+
*/
|
|
21
|
+
export declare const KPT_RAW: Readonly<Record<string, {
|
|
22
|
+
light: string;
|
|
23
|
+
dark: string;
|
|
24
|
+
}>>;
|