ds-tis 1.0.0-beta.10
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 +49 -0
- package/README.md +125 -0
- package/css/base/forced-colors.css +62 -0
- package/css/base/icons.css +73 -0
- package/css/base/index.css +4 -0
- package/css/base/reset.css +80 -0
- package/css/base/typography.css +211 -0
- package/css/components/accordion.css +153 -0
- package/css/components/alert.css +161 -0
- package/css/components/avatar.css +76 -0
- package/css/components/badge.css +83 -0
- package/css/components/breadcrumb.css +58 -0
- package/css/components/button.css +364 -0
- package/css/components/card.css +159 -0
- package/css/components/checkbox.css +296 -0
- package/css/components/combobox.css +330 -0
- package/css/components/divider.css +20 -0
- package/css/components/form-field.css +137 -0
- package/css/components/index.css +28 -0
- package/css/components/input.css +356 -0
- package/css/components/link.css +67 -0
- package/css/components/menu.css +246 -0
- package/css/components/modal.css +236 -0
- package/css/components/pagination.css +132 -0
- package/css/components/radio.css +280 -0
- package/css/components/select.css +399 -0
- package/css/components/skeleton.css +52 -0
- package/css/components/spinner.css +59 -0
- package/css/components/tabs.css +79 -0
- package/css/components/textarea.css +218 -0
- package/css/components/toggle.css +202 -0
- package/css/components/tooltip.css +116 -0
- package/css/design-system.css +13 -0
- package/css/tokens/generated/component.css +720 -0
- package/css/tokens/generated/foundation.css +282 -0
- package/css/tokens/generated/index.css +5 -0
- package/css/tokens/generated/theme-dark.css +243 -0
- package/css/tokens/generated/theme-light.css +244 -0
- package/css/tokens/index.css +6 -0
- package/css/utilities/elevation.css +24 -0
- package/css/utilities/index.css +2 -0
- package/css/utilities/layout.css +132 -0
- package/docs/agent-consumer-usage.en.md +322 -0
- package/docs/agent-consumer-usage.md +294 -0
- package/docs/api/adrs.json +186 -0
- package/docs/api/components.json +2071 -0
- package/docs/api/consumer-context.json +66 -0
- package/docs/api/foundations.json +94 -0
- package/docs/api/tokens.json +6839 -0
- package/docs/llms-full.txt +23699 -0
- package/docs/llms.txt +109 -0
- package/docs/templates/contact.html +364 -0
- package/docs/templates/dashboard.html +318 -0
- package/docs/templates/index.html +232 -0
- package/docs/templates/login.html +286 -0
- package/docs/templates/settings.html +365 -0
- package/docs/templates/signup.html +350 -0
- package/js/accordion.js +192 -0
- package/js/combobox.js +263 -0
- package/js/menu.js +301 -0
- package/js/modal.js +256 -0
- package/js/package.json +3 -0
- package/js/tabs.js +200 -0
- package/js/theme/apply.js +75 -0
- package/js/theme/brand-contrast-audit.js +133 -0
- package/js/theme/color.js +149 -0
- package/js/theme/config-schema.js +40 -0
- package/js/theme/contrast.js +76 -0
- package/js/theme/export.js +118 -0
- package/js/theme/index.js +21 -0
- package/js/theme/overlay.js +29 -0
- package/js/theme/package.json +3 -0
- package/js/theme/palette.js +103 -0
- package/js/theme/radius.js +76 -0
- package/js/theme/semantic-mapper.js +138 -0
- package/js/theme/typography.js +33 -0
- package/js/theme/url-state.js +52 -0
- package/js/tooltip.js +227 -0
- package/package.json +139 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
semantic-mapper.js — ThemeConfig → CSS custom properties
|
|
3
|
+
|
|
4
|
+
Espelha os aliases de tokens/semantic/light.json e dark.json:
|
|
5
|
+
light: brand.bg = brand.600/700/800, links = 700/800, focus = 600
|
|
6
|
+
dark: brand.bg = brand.400/500/400, links = 400/300/200, focus = 500
|
|
7
|
+
|
|
8
|
+
Produz três grupos de variáveis:
|
|
9
|
+
1. Foundation brand scale (--ds-color-brand-50..950)
|
|
10
|
+
2. Overlays toned (--ds-overlay-brand-600 e brand-400)
|
|
11
|
+
3. Semantic com contraste (--ds-brand-content-*, focus, etc.)
|
|
12
|
+
4. Radius + tipografia
|
|
13
|
+
============================================================ */
|
|
14
|
+
|
|
15
|
+
import { generateBrandScale, scaleFromExplicit, STEPS } from './palette.js';
|
|
16
|
+
import { generateTonedOverlays } from './overlay.js';
|
|
17
|
+
import { pickAccessibleForeground, WCAG_AA_UI } from './contrast.js';
|
|
18
|
+
import { generateRadiusTheme } from './radius.js';
|
|
19
|
+
import { buildFontStacks } from './typography.js';
|
|
20
|
+
|
|
21
|
+
/** Neutros do DS usados como foreground candidatos (foundation.color.neutral). */
|
|
22
|
+
const NEUTRAL_LIGHT = '#F8FAFC'; // neutral-50
|
|
23
|
+
const NEUTRAL_DARK = '#0F172A'; // neutral-900
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gera o mapa completo de CSS vars (sem `--` de seletor) para um modo.
|
|
27
|
+
*
|
|
28
|
+
* @param {object} config - ThemeConfig normalizado.
|
|
29
|
+
* @param {'light'|'dark'} mode
|
|
30
|
+
* @returns {{ vars: Record<string,string>, contrast: object }}
|
|
31
|
+
*/
|
|
32
|
+
export function mapThemeToVars(config, mode) {
|
|
33
|
+
const scale = config.brand?.scale
|
|
34
|
+
? { ...generateBrandScale(config.brand.seed), ...scaleFromExplicit(config.brand.scale) }
|
|
35
|
+
: generateBrandScale(config.brand.seed, { chromaBoost: config.brand?.chromaBoost ?? 1 });
|
|
36
|
+
|
|
37
|
+
const vars = {};
|
|
38
|
+
|
|
39
|
+
// 1. Foundation brand scale
|
|
40
|
+
for (const step of STEPS) {
|
|
41
|
+
vars[`--ds-color-brand-${step}`] = scale[step];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. Overlays toned (light usa 600; dark usa 400)
|
|
45
|
+
const light = generateTonedOverlays(scale[600], 'light');
|
|
46
|
+
const dark = generateTonedOverlays(scale[400], 'dark');
|
|
47
|
+
vars['--ds-overlay-brand-600-12'] = light.default;
|
|
48
|
+
vars['--ds-overlay-brand-600-20'] = light.hover;
|
|
49
|
+
vars['--ds-overlay-brand-600-28'] = light.active;
|
|
50
|
+
vars['--ds-overlay-brand-400-15'] = dark.default;
|
|
51
|
+
vars['--ds-overlay-brand-400-25'] = dark.hover;
|
|
52
|
+
vars['--ds-overlay-brand-400-32'] = dark.active;
|
|
53
|
+
|
|
54
|
+
// 3. Semantic brand/toned/link/focus — espelha tokens/semantic/{light,dark}.json
|
|
55
|
+
// e o patch documentado em docs/theming.html (ex.: Sunset).
|
|
56
|
+
let brandFill;
|
|
57
|
+
let fg;
|
|
58
|
+
|
|
59
|
+
if (mode === 'dark') {
|
|
60
|
+
brandFill = scale[400];
|
|
61
|
+
fg = pickAccessibleForeground(brandFill, {
|
|
62
|
+
light: NEUTRAL_LIGHT,
|
|
63
|
+
dark: NEUTRAL_DARK,
|
|
64
|
+
threshold: WCAG_AA_UI,
|
|
65
|
+
});
|
|
66
|
+
vars['--ds-brand-background-default'] = scale[400];
|
|
67
|
+
vars['--ds-brand-background-hover'] = scale[500];
|
|
68
|
+
vars['--ds-brand-background-active'] = scale[400];
|
|
69
|
+
vars['--ds-brand-content-default'] = fg.fg;
|
|
70
|
+
vars['--ds-brand-content-hover'] = fg.fg;
|
|
71
|
+
vars['--ds-toned-background-default'] = dark.default;
|
|
72
|
+
vars['--ds-toned-background-hover'] = dark.hover;
|
|
73
|
+
vars['--ds-toned-background-active'] = dark.active;
|
|
74
|
+
vars['--ds-toned-content-default'] = scale[400];
|
|
75
|
+
vars['--ds-link-content-default'] = scale[400];
|
|
76
|
+
vars['--ds-link-content-hover'] = scale[300];
|
|
77
|
+
vars['--ds-link-content-active'] = scale[200];
|
|
78
|
+
vars['--ds-content-brand'] = scale[400];
|
|
79
|
+
vars['--ds-border-focus'] = scale[500];
|
|
80
|
+
vars['--ds-border-brand'] = scale[500];
|
|
81
|
+
vars['--ds-focus-ring-color'] = scale[500];
|
|
82
|
+
} else {
|
|
83
|
+
brandFill = scale[600];
|
|
84
|
+
fg = pickAccessibleForeground(brandFill, {
|
|
85
|
+
light: NEUTRAL_LIGHT,
|
|
86
|
+
dark: NEUTRAL_DARK,
|
|
87
|
+
threshold: WCAG_AA_UI,
|
|
88
|
+
});
|
|
89
|
+
vars['--ds-brand-background-default'] = scale[600];
|
|
90
|
+
vars['--ds-brand-background-hover'] = scale[700];
|
|
91
|
+
vars['--ds-brand-background-active'] = scale[800];
|
|
92
|
+
vars['--ds-brand-content-default'] = fg.fg;
|
|
93
|
+
vars['--ds-brand-content-hover'] = fg.fg;
|
|
94
|
+
vars['--ds-toned-background-default'] = light.default;
|
|
95
|
+
vars['--ds-toned-background-hover'] = light.hover;
|
|
96
|
+
vars['--ds-toned-background-active'] = light.active;
|
|
97
|
+
vars['--ds-toned-content-default'] = scale[700];
|
|
98
|
+
vars['--ds-link-content-default'] = scale[700];
|
|
99
|
+
vars['--ds-link-content-hover'] = scale[800];
|
|
100
|
+
vars['--ds-link-content-active'] = scale[800];
|
|
101
|
+
vars['--ds-content-brand'] = scale[700];
|
|
102
|
+
vars['--ds-border-focus'] = scale[600];
|
|
103
|
+
vars['--ds-border-brand'] = scale[600];
|
|
104
|
+
vars['--ds-focus-ring-color'] = scale[600];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 4. Radius + tipografia (mode-invariant, mas emitimos em ambos)
|
|
108
|
+
if (config.radius != null) {
|
|
109
|
+
const { foundation, semantic, component } = generateRadiusTheme(config.radius);
|
|
110
|
+
for (const [step, value] of Object.entries(foundation)) {
|
|
111
|
+
vars[`--ds-radius-${step}`] = value;
|
|
112
|
+
}
|
|
113
|
+
if (semantic) {
|
|
114
|
+
for (const [slot, value] of Object.entries(semantic)) {
|
|
115
|
+
vars[`--ds-radius-${slot}`] = value;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const [name, value] of Object.entries(component)) {
|
|
119
|
+
vars[name] = value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (config.typography) {
|
|
123
|
+
const stacks = buildFontStacks(config.typography);
|
|
124
|
+
vars['--ds-font-family-sans'] = stacks.sans;
|
|
125
|
+
vars['--ds-font-family-mono'] = stacks.mono;
|
|
126
|
+
vars['--ds-font-family-display'] = stacks.display;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
vars,
|
|
131
|
+
contrast: {
|
|
132
|
+
brandFill,
|
|
133
|
+
foreground: fg.fg,
|
|
134
|
+
ratio: fg.ratio,
|
|
135
|
+
passes: fg.passes,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
typography.js — monta font stacks preservando fallbacks do DS
|
|
3
|
+
|
|
4
|
+
O customizer troca a fonte principal (sans/mono/display) mas mantém
|
|
5
|
+
a cadeia de fallback do sistema, para não perder robustez quando a
|
|
6
|
+
webfont escolhida não carregar.
|
|
7
|
+
============================================================ */
|
|
8
|
+
|
|
9
|
+
const SANS_FALLBACK =
|
|
10
|
+
"system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
|
|
11
|
+
const MONO_FALLBACK =
|
|
12
|
+
"'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace";
|
|
13
|
+
|
|
14
|
+
/** Envolve o nome da família em aspas se tiver espaço. */
|
|
15
|
+
function quoteFamily(name) {
|
|
16
|
+
const n = String(name).trim();
|
|
17
|
+
if (!n) return '';
|
|
18
|
+
return /\s/.test(n) && !/^['"]/.test(n) ? `'${n}'` : n;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} cfg
|
|
23
|
+
* @param {string} [cfg.sans] - família principal (ex "Inter").
|
|
24
|
+
* @param {string} [cfg.mono] - família mono (ex "DM Mono").
|
|
25
|
+
* @param {string} [cfg.display] - família de display (default: = sans).
|
|
26
|
+
* @returns {{sans:string, mono:string, display:string}} stacks CSS.
|
|
27
|
+
*/
|
|
28
|
+
export function buildFontStacks({ sans = 'Inter', mono = 'DM Mono', display } = {}) {
|
|
29
|
+
const sansStack = `${quoteFamily(sans)}, ${SANS_FALLBACK}`;
|
|
30
|
+
const monoStack = `${quoteFamily(mono)}, ${MONO_FALLBACK}`;
|
|
31
|
+
const displayStack = `${quoteFamily(display || sans)}, ${SANS_FALLBACK}`;
|
|
32
|
+
return { sans: sansStack, mono: monoStack, display: displayStack };
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
url-state.js — encode/decode do ThemeConfig na query string
|
|
3
|
+
|
|
4
|
+
Permite compartilhar presets por link (como ?preset=... do shadcn).
|
|
5
|
+
Usa base64url de um JSON compacto. Funciona em browser e Node
|
|
6
|
+
(usa btoa/atob quando existem, Buffer como fallback).
|
|
7
|
+
============================================================ */
|
|
8
|
+
|
|
9
|
+
import { normalizeConfig } from './config-schema.js';
|
|
10
|
+
|
|
11
|
+
function b64encode(str) {
|
|
12
|
+
if (typeof btoa === 'function') return btoa(unescape(encodeURIComponent(str)));
|
|
13
|
+
return Buffer.from(str, 'utf-8').toString('base64');
|
|
14
|
+
}
|
|
15
|
+
function b64decode(str) {
|
|
16
|
+
if (typeof atob === 'function') return decodeURIComponent(escape(atob(str)));
|
|
17
|
+
return Buffer.from(str, 'base64').toString('utf-8');
|
|
18
|
+
}
|
|
19
|
+
function toUrlSafe(b64) {
|
|
20
|
+
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
21
|
+
}
|
|
22
|
+
function fromUrlSafe(s) {
|
|
23
|
+
let b64 = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
24
|
+
while (b64.length % 4) b64 += '=';
|
|
25
|
+
return b64;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** ThemeConfig → token base64url compacto. */
|
|
29
|
+
export function encodeConfig(config) {
|
|
30
|
+
const cfg = normalizeConfig(config);
|
|
31
|
+
const compact = {
|
|
32
|
+
b: cfg.brand.seed,
|
|
33
|
+
cb: cfg.brand.chromaBoost ?? 1,
|
|
34
|
+
r: cfg.radius,
|
|
35
|
+
fs: cfg.typography.sans,
|
|
36
|
+
fm: cfg.typography.mono,
|
|
37
|
+
m: cfg.mode,
|
|
38
|
+
};
|
|
39
|
+
return toUrlSafe(b64encode(JSON.stringify(compact)));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** token base64url → ThemeConfig normalizado. Lança em token inválido. */
|
|
43
|
+
export function decodeConfig(token) {
|
|
44
|
+
const json = b64decode(fromUrlSafe(token));
|
|
45
|
+
const c = JSON.parse(json);
|
|
46
|
+
return normalizeConfig({
|
|
47
|
+
brand: { seed: c.b, chromaBoost: c.cb },
|
|
48
|
+
radius: c.r,
|
|
49
|
+
typography: { sans: c.fs, mono: c.fm },
|
|
50
|
+
mode: c.m,
|
|
51
|
+
});
|
|
52
|
+
}
|
package/js/tooltip.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
tooltip.js — runtime público para Tooltip (required)
|
|
3
|
+
|
|
4
|
+
Anatomia e estados visuais: css/components/tooltip.css
|
|
5
|
+
Uso:
|
|
6
|
+
<div class="ds-tooltip ds-tooltip--top">
|
|
7
|
+
<button type="button" aria-describedby="tip-1">…</button>
|
|
8
|
+
<span class="ds-tooltip__content" id="tip-1" role="tooltip">…</span>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
Ciclo de vida:
|
|
12
|
+
const instances = initTooltips(root);
|
|
13
|
+
destroyTooltips(root); // ou instance.destroy()
|
|
14
|
+
|
|
15
|
+
WCAG 1.4.13: dismissible (Escape), hoverable (pointer no content),
|
|
16
|
+
persistent enquanto hover/focus ativo.
|
|
17
|
+
============================================================ */
|
|
18
|
+
|
|
19
|
+
const instances = new Set();
|
|
20
|
+
const SHOW_DELAY_MS = 100;
|
|
21
|
+
const HIDE_DELAY_MS = 100;
|
|
22
|
+
let generatedId = 0;
|
|
23
|
+
|
|
24
|
+
function emit(root, name, detail) {
|
|
25
|
+
root.dispatchEvent(new CustomEvent(name, { bubbles: true, detail }));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getTrigger(root, content) {
|
|
29
|
+
return [...root.children].find((child) => child !== content) || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createInstance(root) {
|
|
33
|
+
const content = root.querySelector(':scope > .ds-tooltip__content[role="tooltip"], :scope > .ds-tooltip__content');
|
|
34
|
+
if (!content) return null;
|
|
35
|
+
if (!content.hasAttribute('role')) content.setAttribute('role', 'tooltip');
|
|
36
|
+
|
|
37
|
+
const trigger = getTrigger(root, content);
|
|
38
|
+
if (!trigger) return null;
|
|
39
|
+
|
|
40
|
+
if (!content.id) {
|
|
41
|
+
do {
|
|
42
|
+
generatedId += 1;
|
|
43
|
+
content.id = `ds-tooltip-${generatedId}`;
|
|
44
|
+
} while (content.ownerDocument.getElementById(content.id) !== content);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const describedBy = (trigger.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
|
|
48
|
+
if (!describedBy.includes(content.id)) {
|
|
49
|
+
trigger.setAttribute('aria-describedby', [...describedBy, content.id].join(' '));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const cleanups = [];
|
|
53
|
+
const on = (target, type, handler, options) => {
|
|
54
|
+
target.addEventListener(type, handler, options);
|
|
55
|
+
cleanups.push(() => target.removeEventListener(type, handler, options));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let showTimer = null;
|
|
59
|
+
let hideTimer = null;
|
|
60
|
+
let focusFrame = null;
|
|
61
|
+
let open = root.dataset.open === 'true';
|
|
62
|
+
let suppressUntilLeave = false;
|
|
63
|
+
const view = root.ownerDocument.defaultView;
|
|
64
|
+
|
|
65
|
+
const clearTimers = () => {
|
|
66
|
+
if (showTimer) {
|
|
67
|
+
clearTimeout(showTimer);
|
|
68
|
+
showTimer = null;
|
|
69
|
+
}
|
|
70
|
+
if (hideTimer) {
|
|
71
|
+
clearTimeout(hideTimer);
|
|
72
|
+
hideTimer = null;
|
|
73
|
+
}
|
|
74
|
+
if (focusFrame) {
|
|
75
|
+
view.cancelAnimationFrame(focusFrame);
|
|
76
|
+
focusFrame = null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const inst = {
|
|
81
|
+
root,
|
|
82
|
+
show() {
|
|
83
|
+
clearTimers();
|
|
84
|
+
if (suppressUntilLeave || open) return;
|
|
85
|
+
open = true;
|
|
86
|
+
root.dataset.open = 'true';
|
|
87
|
+
content.removeAttribute('hidden');
|
|
88
|
+
emit(root, 'ds-tooltip-show', { root, trigger, content });
|
|
89
|
+
},
|
|
90
|
+
hide() {
|
|
91
|
+
clearTimers();
|
|
92
|
+
if (!open) return;
|
|
93
|
+
open = false;
|
|
94
|
+
delete root.dataset.open;
|
|
95
|
+
content.setAttribute('hidden', '');
|
|
96
|
+
emit(root, 'ds-tooltip-hide', { root, trigger, content });
|
|
97
|
+
},
|
|
98
|
+
scheduleShow() {
|
|
99
|
+
clearTimers();
|
|
100
|
+
if (suppressUntilLeave || open) return;
|
|
101
|
+
showTimer = setTimeout(() => {
|
|
102
|
+
showTimer = null;
|
|
103
|
+
inst.show();
|
|
104
|
+
}, SHOW_DELAY_MS);
|
|
105
|
+
},
|
|
106
|
+
scheduleHide() {
|
|
107
|
+
clearTimers();
|
|
108
|
+
if (!open) return;
|
|
109
|
+
hideTimer = setTimeout(() => {
|
|
110
|
+
hideTimer = null;
|
|
111
|
+
if (root.contains(root.ownerDocument.activeElement)) return;
|
|
112
|
+
inst.hide();
|
|
113
|
+
}, HIDE_DELAY_MS);
|
|
114
|
+
},
|
|
115
|
+
destroy() {
|
|
116
|
+
clearTimers();
|
|
117
|
+
if (open) {
|
|
118
|
+
open = false;
|
|
119
|
+
delete root.dataset.open;
|
|
120
|
+
content.setAttribute('hidden', '');
|
|
121
|
+
}
|
|
122
|
+
while (cleanups.length) cleanups.pop()();
|
|
123
|
+
delete root.dataset.dsTooltipInit;
|
|
124
|
+
instances.delete(inst);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Start closed unless the markup already requests open (static docs demos).
|
|
129
|
+
if (root.dataset.open === 'true') {
|
|
130
|
+
open = true;
|
|
131
|
+
content.removeAttribute('hidden');
|
|
132
|
+
} else {
|
|
133
|
+
open = false;
|
|
134
|
+
content.setAttribute('hidden', '');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
on(root, 'pointerenter', () => {
|
|
138
|
+
suppressUntilLeave = false;
|
|
139
|
+
inst.scheduleShow();
|
|
140
|
+
});
|
|
141
|
+
on(root, 'pointerleave', () => {
|
|
142
|
+
suppressUntilLeave = false;
|
|
143
|
+
inst.scheduleHide();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
on(trigger, 'focusin', () => {
|
|
147
|
+
if (!suppressUntilLeave) inst.show();
|
|
148
|
+
});
|
|
149
|
+
on(trigger, 'focusout', () => {
|
|
150
|
+
if (focusFrame) view.cancelAnimationFrame(focusFrame);
|
|
151
|
+
focusFrame = view.requestAnimationFrame(() => {
|
|
152
|
+
focusFrame = null;
|
|
153
|
+
if (!instances.has(inst)) return;
|
|
154
|
+
if (root.contains(root.ownerDocument.activeElement)) return;
|
|
155
|
+
suppressUntilLeave = false;
|
|
156
|
+
inst.scheduleHide();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
on(root.ownerDocument, 'keydown', (e) => {
|
|
161
|
+
if (!open) return;
|
|
162
|
+
if (e.key === 'Escape') {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
suppressUntilLeave = true;
|
|
165
|
+
inst.hide();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return inst;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function isInside(root, node) {
|
|
173
|
+
return root === document || root === node || (typeof root.contains === 'function' && root.contains(node));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {ParentNode} [root]
|
|
178
|
+
*/
|
|
179
|
+
export function initTooltips(root = document) {
|
|
180
|
+
const created = [];
|
|
181
|
+
const tooltips = [];
|
|
182
|
+
|
|
183
|
+
if (root instanceof Element && root.matches('.ds-tooltip')) tooltips.push(root);
|
|
184
|
+
if (typeof root.querySelectorAll === 'function') {
|
|
185
|
+
tooltips.push(...root.querySelectorAll('.ds-tooltip'));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
tooltips.forEach((tooltip) => {
|
|
189
|
+
if (tooltip.dataset.dsTooltipInit === 'true') return;
|
|
190
|
+
if (tooltip.closest('[inert]')) return;
|
|
191
|
+
const inst = createInstance(tooltip);
|
|
192
|
+
if (inst) {
|
|
193
|
+
tooltip.dataset.dsTooltipInit = 'true';
|
|
194
|
+
instances.add(inst);
|
|
195
|
+
created.push(inst);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return created;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @param {ParentNode} [root]
|
|
204
|
+
*/
|
|
205
|
+
export function destroyTooltips(root = document) {
|
|
206
|
+
for (const inst of [...instances]) {
|
|
207
|
+
if (isInside(root, inst.root)) inst.destroy();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @param {HTMLElement} root
|
|
213
|
+
*/
|
|
214
|
+
export function showTooltip(root) {
|
|
215
|
+
const inst = [...instances].find((item) => item.root === root);
|
|
216
|
+
inst?.show();
|
|
217
|
+
return inst;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @param {HTMLElement} root
|
|
222
|
+
*/
|
|
223
|
+
export function hideTooltip(root) {
|
|
224
|
+
const inst = [...instances].find((item) => item.root === root);
|
|
225
|
+
inst?.hide();
|
|
226
|
+
return inst;
|
|
227
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ds-tis",
|
|
3
|
+
"version": "1.0.0-beta.10",
|
|
4
|
+
"description": "White-label CSS design system with DTCG tokens, component contracts, and light/dark mode",
|
|
5
|
+
"homepage": "https://tis-experience.github.io/ds-tis/",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/tis-experience/ds-tis.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/tis-experience/ds-tis/issues"
|
|
12
|
+
},
|
|
13
|
+
"main": "css/design-system.css",
|
|
14
|
+
"style": "css/design-system.css",
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"**/*.css"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./css/design-system.css",
|
|
20
|
+
"./css": "./css/design-system.css",
|
|
21
|
+
"./css/design-system.css": "./css/design-system.css",
|
|
22
|
+
"./accordion": "./js/accordion.js",
|
|
23
|
+
"./accordion.js": "./js/accordion.js",
|
|
24
|
+
"./combobox": "./js/combobox.js",
|
|
25
|
+
"./combobox.js": "./js/combobox.js",
|
|
26
|
+
"./modal": "./js/modal.js",
|
|
27
|
+
"./modal.js": "./js/modal.js",
|
|
28
|
+
"./menu": "./js/menu.js",
|
|
29
|
+
"./menu.js": "./js/menu.js",
|
|
30
|
+
"./tabs": "./js/tabs.js",
|
|
31
|
+
"./tabs.js": "./js/tabs.js",
|
|
32
|
+
"./tooltip": "./js/tooltip.js",
|
|
33
|
+
"./tooltip.js": "./js/tooltip.js",
|
|
34
|
+
"./theme": "./js/theme/index.js",
|
|
35
|
+
"./theme/*": "./js/theme/*.js",
|
|
36
|
+
"./metadata": "./docs/api/consumer-context.json",
|
|
37
|
+
"./metadata/components": "./docs/api/components.json",
|
|
38
|
+
"./metadata/tokens": "./docs/api/tokens.json",
|
|
39
|
+
"./metadata/foundations": "./docs/api/foundations.json",
|
|
40
|
+
"./metadata/adrs": "./docs/api/adrs.json",
|
|
41
|
+
"./agent-guide": "./docs/agent-consumer-usage.md",
|
|
42
|
+
"./agent-guide/en": "./docs/agent-consumer-usage.en.md",
|
|
43
|
+
"./llms": "./docs/llms.txt",
|
|
44
|
+
"./llms-full": "./docs/llms-full.txt",
|
|
45
|
+
"./templates/*": "./docs/templates/*",
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"css/",
|
|
50
|
+
"docs/agent-consumer-usage.md",
|
|
51
|
+
"docs/agent-consumer-usage.en.md",
|
|
52
|
+
"docs/api/adrs.json",
|
|
53
|
+
"docs/api/components.json",
|
|
54
|
+
"docs/api/consumer-context.json",
|
|
55
|
+
"docs/api/foundations.json",
|
|
56
|
+
"docs/api/tokens.json",
|
|
57
|
+
"docs/llms.txt",
|
|
58
|
+
"docs/llms-full.txt",
|
|
59
|
+
"docs/templates/",
|
|
60
|
+
"js/accordion.js",
|
|
61
|
+
"js/combobox.js",
|
|
62
|
+
"js/menu.js",
|
|
63
|
+
"js/modal.js",
|
|
64
|
+
"js/tabs.js",
|
|
65
|
+
"js/tooltip.js",
|
|
66
|
+
"js/package.json",
|
|
67
|
+
"js/theme/"
|
|
68
|
+
],
|
|
69
|
+
"keywords": [
|
|
70
|
+
"design-system",
|
|
71
|
+
"css",
|
|
72
|
+
"tokens",
|
|
73
|
+
"white-label",
|
|
74
|
+
"dark-mode"
|
|
75
|
+
],
|
|
76
|
+
"author": "TIS Experience Engineering",
|
|
77
|
+
"license": "UNLICENSED",
|
|
78
|
+
"engines": {
|
|
79
|
+
"node": ">=22"
|
|
80
|
+
},
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public",
|
|
83
|
+
"tag": "beta"
|
|
84
|
+
},
|
|
85
|
+
"scripts": {
|
|
86
|
+
"build:tokens": "node build-tokens.mjs",
|
|
87
|
+
"sync:docs": "node scripts/sync-docs.mjs",
|
|
88
|
+
"verify:tokens": "node scripts/tokens-verify.mjs",
|
|
89
|
+
"build:api": "node scripts/build-api.mjs",
|
|
90
|
+
"build:icons": "node scripts/build-icons.mjs",
|
|
91
|
+
"build:llms": "node scripts/build-llms.mjs",
|
|
92
|
+
"build:all": "npm run build:tokens && npm run build:icons && npm run sync:docs && npm run build:api && npm run build:llms && npm run verify:tokens && npm test",
|
|
93
|
+
"prepublishOnly": "npm run build:all && npm run test:app-ready -- --release",
|
|
94
|
+
"pack:check": "npm pack --dry-run && npm run test:audit-scenarios && npm run test:app-ready",
|
|
95
|
+
"test": "node scripts/test-token-integrity.mjs && node scripts/test-css-references.mjs && node scripts/test-generators.mjs && node scripts/test-docs-consumption.mjs && node scripts/test-accordion-docs.mjs && node scripts/test-combobox-docs.mjs && node scripts/test-pagination-docs.mjs && node scripts/test-field-docs.mjs && node scripts/test-interactive.mjs && node scripts/test-api-runtime.mjs && node scripts/test-consumer-context.mjs && node scripts/test-readiness-evidence.mjs && node scripts/test-component-readiness.mjs && node scripts/test-runtime-lifecycle.mjs && node scripts/test-theme-engine.mjs && node scripts/test-a11y.mjs && npm run test:a11y:theme-playground",
|
|
96
|
+
"test:app-ready": "node scripts/test-app-ready.mjs",
|
|
97
|
+
"test:audit-scenarios": "node scripts/test-audit-scenarios.mjs",
|
|
98
|
+
"test:audit-scenarios:ci": "node scripts/test-audit-scenarios.mjs --skip-local-figma",
|
|
99
|
+
"test:consumer-smoke": "node scripts/test-consumer-smoke.mjs",
|
|
100
|
+
"test:docs-consumption": "node scripts/test-docs-consumption.mjs",
|
|
101
|
+
"test:runtime-lifecycle": "node scripts/test-runtime-lifecycle.mjs",
|
|
102
|
+
"test:self": "node scripts/test-self.mjs",
|
|
103
|
+
"test:readiness": "node scripts/test-component-readiness.mjs",
|
|
104
|
+
"test:readiness:evidence": "node scripts/test-readiness-evidence.mjs",
|
|
105
|
+
"test:theme": "node scripts/test-theme-engine.mjs",
|
|
106
|
+
"test:a11y": "node scripts/test-a11y.mjs",
|
|
107
|
+
"test:a11y:theme-playground": "node scripts/test-a11y.mjs --strict-load --server --filter theme-playground --zero-blocking",
|
|
108
|
+
"test:visual": "node scripts/test-visual.mjs",
|
|
109
|
+
"test:visual:update": "node scripts/test-visual.mjs --update",
|
|
110
|
+
"sync:tokens-from-figma": "node scripts/sync-tokens-from-figma.mjs",
|
|
111
|
+
"sync:tokens-from-figma:write": "node scripts/sync-tokens-from-figma.mjs --write",
|
|
112
|
+
"build:registry": "node scripts/build-token-registry.mjs",
|
|
113
|
+
"build:registry:init": "node scripts/build-token-registry.mjs --init",
|
|
114
|
+
"verify:registry": "node scripts/build-token-registry.mjs --check",
|
|
115
|
+
"security:check": "node scripts/security-check.mjs",
|
|
116
|
+
"figma:plugin:check": "node --check figma-plugin/snapshot-exporter/code.js",
|
|
117
|
+
"figma:snapshot:install": "node scripts/install-figma-snapshot.mjs",
|
|
118
|
+
"figma:snapshot:refresh": "node scripts/refresh-figma-snapshot.mjs",
|
|
119
|
+
"verify:figma-structure": "node scripts/verify-figma-structure.mjs",
|
|
120
|
+
"audit:component-tokens": "node scripts/verify-figma-structure.mjs --strict-unused",
|
|
121
|
+
"agent:preflight": "node scripts/agent-preflight.mjs",
|
|
122
|
+
"verify:agent-docs": "node scripts/verify-agent-docs.mjs",
|
|
123
|
+
"agents:create-run": "node scripts/agents/create-run.mjs",
|
|
124
|
+
"agents:validate-run": "node scripts/agents/validate-run.mjs",
|
|
125
|
+
"agents:next-step": "node scripts/agents/next-step.mjs",
|
|
126
|
+
"agents:gate": "node scripts/agents/gate.mjs",
|
|
127
|
+
"agents:validate-matrix": "node scripts/agents/validate-matrix.mjs"
|
|
128
|
+
},
|
|
129
|
+
"devDependencies": {
|
|
130
|
+
"@axe-core/playwright": "^4.11.2",
|
|
131
|
+
"@tokens-studio/sd-transforms": "^2.0.3",
|
|
132
|
+
"lucide": "^1.18.0",
|
|
133
|
+
"marked": "^18.0.2",
|
|
134
|
+
"pixelmatch": "^7.1.0",
|
|
135
|
+
"playwright": "^1.59.1",
|
|
136
|
+
"pngjs": "^7.0.0",
|
|
137
|
+
"style-dictionary": "^5.4.0"
|
|
138
|
+
}
|
|
139
|
+
}
|