Reset
{ setImportText(themeJson); setImportVisible(true); }}>Import
diff --git a/apps/docs/src/containers/theme-studio/preview-components.tsx b/apps/docs/src/containers/theme-studio/preview-components.tsx
index 1dffbe93..9c6344cc 100644
--- a/apps/docs/src/containers/theme-studio/preview-components.tsx
+++ b/apps/docs/src/containers/theme-studio/preview-components.tsx
@@ -658,7 +658,7 @@ function DashboardPreview(): React.ReactElement {
function MailPreview(): React.ReactElement {
return (
-
+
@@ -778,7 +778,7 @@ function MailPreview(): React.ReactElement {
-
+
);
}
diff --git a/apps/docs/src/containers/theme-studio/theme-document-adapter.ts b/apps/docs/src/containers/theme-studio/theme-document-adapter.ts
index 9e346e63..26a99ba8 100644
--- a/apps/docs/src/containers/theme-studio/theme-document-adapter.ts
+++ b/apps/docs/src/containers/theme-studio/theme-document-adapter.ts
@@ -23,7 +23,10 @@ export function inferPresetIdFromThemeDocument(theme: ThemeDocument): string {
}
function normalizeImportedThemeDocument(theme: ThemeDocument): ThemeDocument {
- const validation = validateThemeDocument(theme);
+ const validation = validateThemeDocument(theme, { strict: true });
+ if (!validation.valid) {
+ throw new Error(validation.errors.join('\n'));
+ }
return validation.normalizedDocument as ThemeDocument;
}
@@ -113,42 +116,15 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
'line-height-base': fields.lineHeightBase,
'h1-font-size': fields.h1Size,
'h2-font-size': fields.h2Size,
- 'letter-spacing': fields.letterSpacing,
'border-radius': fields.radius,
'shadow-card': fields.shadowCard,
'shadow-focus': fields.shadowFocus,
- 'editor-primary-foreground': fields.primaryForeground,
- 'editor-secondary': fields.secondary,
- 'editor-secondary-foreground': fields.secondaryForeground,
- 'editor-accent': fields.accent,
- 'editor-accent-foreground': fields.accentForeground,
- 'editor-success': fields.success,
- 'editor-success-foreground': fields.successForeground,
- 'editor-info': fields.info,
- 'editor-info-foreground': fields.infoForeground,
- 'editor-warning': fields.warning,
- 'editor-warning-foreground': fields.warningForeground,
- 'editor-danger': fields.danger,
- 'editor-danger-foreground': fields.dangerForeground,
- 'editor-base': fields.base,
- 'editor-base-foreground': fields.baseForeground,
- 'editor-card': fields.card,
- 'editor-card-foreground': fields.cardForeground,
- 'editor-popover': fields.popover,
- 'editor-popover-foreground': fields.popoverForeground,
- 'editor-muted': fields.muted,
- 'editor-muted-foreground': fields.mutedForeground,
- 'editor-border': fields.border,
- 'editor-input': fields.input,
- 'editor-ring': fields.ring,
- 'editor-sidebar': fields.sidebar,
- 'editor-sidebar-foreground': fields.sidebarForeground,
- 'editor-sidebar-primary': fields.sidebarPrimary,
- 'editor-sidebar-primary-foreground': fields.sidebarPrimaryForeground,
- 'editor-sidebar-accent': fields.sidebarAccent,
- 'editor-sidebar-accent-foreground': fields.sidebarAccentForeground,
- 'editor-sidebar-border': fields.sidebarBorder,
- 'editor-sidebar-ring': fields.sidebarRing,
+ 'control.height.sm': fields.fieldHeightSm,
+ 'control.height.md': fields.fieldHeightMd,
+ 'control.height.lg': fields.fieldHeightLg,
+ 'control.padding-inline.sm': fields.fieldPaddingSm,
+ 'control.padding-inline.md': fields.fieldPaddingMd,
+ 'control.padding-inline.lg': fields.fieldPaddingLg,
},
components: {
'button.bg.primary': fields.primary,
@@ -164,12 +140,6 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
'button.text.default': fields.baseForeground,
'button.text.default-hover': fields.baseForeground,
'button.text.default-active': fields.baseForeground,
- 'control.height.sm': fields.fieldHeightSm,
- 'control.height.md': fields.fieldHeightMd,
- 'control.height.lg': fields.fieldHeightLg,
- 'control.padding-inline.sm': fields.fieldPaddingSm,
- 'control.padding-inline.md': fields.fieldPaddingMd,
- 'control.padding-inline.lg': fields.fieldPaddingLg,
'button.radius': fields.buttonRadius,
'button.height.sm': fields.buttonHeightSm,
'button.height.md': fields.buttonHeightMd,
@@ -230,25 +200,25 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
'picker.input-padding.lg': `0 ${fields.fieldPaddingLg}`,
'picker.dropdown-bg': fields.popover,
'picker.dropdown-radius': fields.cardRadius,
- 'picker.header-border': fields.border,
- 'picker.header-button-color': fields.mutedForeground,
- 'picker.header-button-color-hover': fields.primary,
- 'picker.today-color': fields.primary,
- 'picker.today-color-hover': fields.primary,
+ 'date-picker.header-border': fields.border,
+ 'date-picker.header-button-color': fields.mutedForeground,
+ 'date-picker.header-button-color-hover': fields.primary,
+ 'date-picker.today-color': fields.primary,
+ 'date-picker.today-color-hover': fields.primary,
'picker.cell-hover-bg': fields.muted,
- 'picker.cell-selected-bg': fields.primary,
- 'picker.cell-selected-color': fields.primaryForeground,
- 'picker.cell-selected-hover-bg': fields.primary,
- 'picker.cell-today-border': fields.primary,
- 'picker.cell-radius': fields.inputRadius,
- 'picker.range-bg': fields.accent,
- 'picker.time-column-border': fields.border,
- 'picker.time-cell-bg-selected': fields.primary,
- 'picker.time-cell-bg-selected-hover': fields.primary,
- 'picker.ok-button-bg': fields.primary,
- 'picker.ok-button-bg-hover': fields.primary,
- 'picker.ok-button-color': fields.primaryForeground,
- 'picker.ok-button-radius': fields.buttonRadius,
+ 'date-picker.cell-selected-bg': fields.primary,
+ 'date-picker.cell-selected-color': fields.primaryForeground,
+ 'date-picker.cell-selected-hover-bg': fields.primary,
+ 'date-picker.cell-today-border': fields.primary,
+ 'date-picker.cell-radius': fields.inputRadius,
+ 'date-picker.range-bg': fields.accent,
+ 'time-picker.column-border': fields.border,
+ 'time-picker.cell-bg-selected': fields.primary,
+ 'time-picker.cell-bg-selected-hover': fields.primary,
+ 'time-picker.ok-button-bg': fields.primary,
+ 'time-picker.ok-button-bg-hover': fields.primary,
+ 'time-picker.ok-button-color': fields.primaryForeground,
+ 'time-picker.ok-button-radius': fields.buttonRadius,
'calendar.bg': fields.card,
'calendar.border': fields.border,
'calendar.radius': fields.cardRadius,
@@ -377,50 +347,37 @@ export function buildDraftFromThemeDocument(theme: ThemeDocument): ThemeEditorDr
fields: {
...baseFields,
primary: readToken(normalizedTheme, 'color-primary') ?? baseFields.primary,
- primaryForeground: readToken(normalizedTheme, 'editor-primary-foreground', 'button.text.primary') ?? baseFields.primaryForeground,
- secondary: readToken(normalizedTheme, 'editor-secondary') ?? readToken(normalizedTheme, 'color-fill', 'button.bg.default') ?? baseFields.secondary,
- secondaryForeground: readToken(normalizedTheme, 'editor-secondary-foreground', 'button.text.default') ?? baseFields.secondaryForeground,
- accent: readToken(normalizedTheme, 'editor-accent') ?? readToken(normalizedTheme, 'color-primary-bg') ?? baseFields.accent,
- accentForeground: readToken(normalizedTheme, 'editor-accent-foreground') ?? baseFields.accentForeground,
- success: readToken(normalizedTheme, 'editor-success') ?? readToken(normalizedTheme, 'color-success') ?? baseFields.success,
- successForeground: readToken(normalizedTheme, 'editor-success-foreground') ?? baseFields.successForeground,
- info: readToken(normalizedTheme, 'editor-info') ?? readToken(normalizedTheme, 'color-info') ?? baseFields.info,
- infoForeground: readToken(normalizedTheme, 'editor-info-foreground') ?? baseFields.infoForeground,
- warning: readToken(normalizedTheme, 'editor-warning') ?? readToken(normalizedTheme, 'color-warning') ?? baseFields.warning,
- warningForeground: readToken(normalizedTheme, 'editor-warning-foreground') ?? baseFields.warningForeground,
- danger: readToken(normalizedTheme, 'editor-danger') ?? readToken(normalizedTheme, 'color-danger') ?? baseFields.danger,
- dangerForeground: readToken(normalizedTheme, 'editor-danger-foreground') ?? baseFields.dangerForeground,
- base: readToken(normalizedTheme, 'editor-base') ?? readToken(normalizedTheme, 'color-bg') ?? baseFields.base,
- baseForeground: readToken(normalizedTheme, 'editor-base-foreground') ?? readToken(normalizedTheme, 'color-text') ?? baseFields.baseForeground,
- card: readToken(normalizedTheme, 'editor-card') ?? readToken(normalizedTheme, 'color-bg-container', 'card.bg') ?? baseFields.card,
- cardForeground: readToken(normalizedTheme, 'editor-card-foreground', 'card.header-color') ?? baseFields.cardForeground,
- popover: readToken(normalizedTheme, 'editor-popover') ?? readToken(normalizedTheme, 'color-bg-elevated') ?? baseFields.popover,
- popoverForeground: readToken(normalizedTheme, 'editor-popover-foreground') ?? baseFields.popoverForeground,
- muted: readToken(normalizedTheme, 'editor-muted') ?? readToken(normalizedTheme, 'color-bg-spotlight') ?? baseFields.muted,
- mutedForeground: readToken(normalizedTheme, 'editor-muted-foreground') ?? readToken(normalizedTheme, 'color-text-secondary') ?? baseFields.mutedForeground,
- border: readToken(normalizedTheme, 'editor-border') ?? readToken(normalizedTheme, 'color-border') ?? baseFields.border,
- input: readToken(normalizedTheme, 'editor-input') ?? readToken(normalizedTheme, 'color-border', 'input.border') ?? baseFields.input,
- ring: readToken(normalizedTheme, 'editor-ring', 'input.border.focus') ?? baseFields.ring,
+ primaryForeground: readComponentFirst(normalizedTheme, 'button.text.primary') ?? baseFields.primaryForeground,
+ secondary: readToken(normalizedTheme, 'color-fill', 'button.bg.default') ?? baseFields.secondary,
+ secondaryForeground: readToken(normalizedTheme, 'color-text', 'button.text.default') ?? baseFields.secondaryForeground,
+ accent: readToken(normalizedTheme, 'color-primary-bg') ?? baseFields.accent,
+ success: readToken(normalizedTheme, 'color-success') ?? baseFields.success,
+ info: readToken(normalizedTheme, 'color-info') ?? baseFields.info,
+ warning: readToken(normalizedTheme, 'color-warning') ?? baseFields.warning,
+ danger: readToken(normalizedTheme, 'color-danger') ?? baseFields.danger,
+ base: readToken(normalizedTheme, 'color-bg') ?? baseFields.base,
+ baseForeground: readToken(normalizedTheme, 'color-text') ?? baseFields.baseForeground,
+ card: readToken(normalizedTheme, 'color-bg-container', 'card.bg') ?? baseFields.card,
+ cardForeground: readToken(normalizedTheme, 'color-text-heading', 'card.header-color') ?? baseFields.cardForeground,
+ popover: readToken(normalizedTheme, 'color-bg-elevated') ?? baseFields.popover,
+ muted: readToken(normalizedTheme, 'color-bg-spotlight') ?? baseFields.muted,
+ mutedForeground: readToken(normalizedTheme, 'color-text-secondary') ?? baseFields.mutedForeground,
+ border: readToken(normalizedTheme, 'color-border') ?? baseFields.border,
+ input: readToken(normalizedTheme, 'color-border', 'input.border') ?? baseFields.input,
+ ring: readToken(normalizedTheme, 'color-primary', 'input.border.focus') ?? baseFields.ring,
chart1: readToken(normalizedTheme, 'chart-1') ?? baseFields.chart1,
chart2: readToken(normalizedTheme, 'chart-2') ?? baseFields.chart2,
chart3: readToken(normalizedTheme, 'chart-3') ?? baseFields.chart3,
chart4: readToken(normalizedTheme, 'chart-4') ?? baseFields.chart4,
chart5: readToken(normalizedTheme, 'chart-5') ?? baseFields.chart5,
- sidebar: readToken(normalizedTheme, 'editor-sidebar', 'layout.sidebar-bg') ?? baseFields.sidebar,
- sidebarForeground: readToken(normalizedTheme, 'editor-sidebar-foreground', 'layout.sidebar-color') ?? baseFields.sidebarForeground,
- sidebarPrimary: readToken(normalizedTheme, 'editor-sidebar-primary') ?? baseFields.sidebarPrimary,
- sidebarPrimaryForeground: readToken(normalizedTheme, 'editor-sidebar-primary-foreground') ?? baseFields.sidebarPrimaryForeground,
- sidebarAccent: readToken(normalizedTheme, 'editor-sidebar-accent') ?? baseFields.sidebarAccent,
- sidebarAccentForeground: readToken(normalizedTheme, 'editor-sidebar-accent-foreground') ?? baseFields.sidebarAccentForeground,
- sidebarBorder: readToken(normalizedTheme, 'editor-sidebar-border') ?? baseFields.sidebarBorder,
- sidebarRing: readToken(normalizedTheme, 'editor-sidebar-ring') ?? baseFields.sidebarRing,
+ sidebar: readToken(normalizedTheme, 'color-bg-layout', 'layout.sidebar-bg') ?? baseFields.sidebar,
+ sidebarForeground: readToken(normalizedTheme, 'color-text', 'layout.sidebar-color') ?? baseFields.sidebarForeground,
fontSans: readToken(normalizedTheme, 'font-family') ?? baseFields.fontSans,
fontMono: readToken(normalizedTheme, 'font-family-monospace') ?? baseFields.fontMono,
fontSizeBase: readToken(normalizedTheme, 'font-size-base') ?? baseFields.fontSizeBase,
lineHeightBase: readToken(normalizedTheme, 'line-height-base') ?? baseFields.lineHeightBase,
h1Size: readToken(normalizedTheme, 'h1-font-size') ?? baseFields.h1Size,
h2Size: readToken(normalizedTheme, 'h2-font-size') ?? baseFields.h2Size,
- letterSpacing: readToken(normalizedTheme, 'letter-spacing') ?? baseFields.letterSpacing,
radius: readToken(normalizedTheme, 'border-radius') ?? baseFields.radius,
shadowCard: readToken(normalizedTheme, 'shadow-card', 'card.shadow') ?? baseFields.shadowCard,
shadowFocus: readToken(normalizedTheme, 'shadow-focus', 'input.shadow.focus') ?? toRgba(baseFields.primary, 0.22),
diff --git a/apps/docs/src/containers/theme-studio/theme-studio.scss b/apps/docs/src/containers/theme-studio/theme-studio.scss
index b248d5ba..1ce00fa7 100644
--- a/apps/docs/src/containers/theme-studio/theme-studio.scss
+++ b/apps/docs/src/containers/theme-studio/theme-studio.scss
@@ -11,7 +11,7 @@
.theme-studio__topbar {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
- gap: 8px;
+ gap: 12px;
padding: 8px;
border: 1px solid var(--ty-color-border-secondary, var(--ty-color-border));
border-radius: 12px;
@@ -21,13 +21,11 @@
box-shadow: 0 6px 16px rgba(17, 24, 39, 0.03);
}
-.theme-studio__topbar-copy {
+.theme-studio__topbar-primary {
min-width: 0;
display: flex;
align-items: center;
justify-content: flex-start;
- gap: 10px;
- flex-direction: row;
}
.theme-studio__topbar-actions,
@@ -91,8 +89,8 @@
min-width: auto;
}
-.theme-studio__topbar-actions .theme-studio__select {
- width: 220px;
+.theme-studio__topbar-primary .theme-studio__select {
+ width: min(360px, 100%);
}
.theme-studio__eyebrow {
@@ -103,23 +101,6 @@
white-space: nowrap;
}
-.theme-studio__topbar-meta {
- display: flex;
- gap: 6px;
- flex-wrap: wrap;
- justify-content: flex-start;
-}
-
-.theme-studio__topbar-meta span {
- padding: 4px 8px;
- border-radius: 999px;
- background: color-mix(in srgb, var(--editor-primary), transparent 94%);
- border: 1px solid color-mix(in srgb, var(--editor-primary), transparent 88%);
- font-size: 10px;
- line-height: 1;
- text-transform: capitalize;
-}
-
.theme-studio__topbar-utility {
display: flex;
align-items: center;
@@ -977,8 +958,9 @@
}
.theme-studio__mail-shell {
- grid-template-columns: 220px 320px minmax(0, 1fr);
- gap: 16px;
+ grid-template-columns: minmax(190px, 0.72fr) minmax(260px, 1fr) minmax(380px, 1.55fr);
+ gap: 14px;
+ align-items: stretch;
}
.theme-studio__dashboard-sidebar {
@@ -1091,9 +1073,11 @@
display: flex;
flex-direction: column;
gap: 18px;
+ min-width: 0;
background: color-mix(in srgb, var(--editor-sidebar), var(--editor-base) 8%);
color: var(--editor-sidebar-foreground);
border: 1px solid var(--editor-sidebar-border);
+ overflow: hidden;
}
.theme-studio__mail-sidebar .theme-studio__eyebrow,
@@ -1120,9 +1104,15 @@
padding-bottom: 2px;
}
+.theme-studio__mail-sidebar-head .ty-typography-heading {
+ margin: 0;
+ overflow-wrap: anywhere;
+}
+
.theme-studio__mail-panel,
.theme-studio__mail-detail {
min-width: 0;
+ overflow: hidden;
}
.theme-studio__mail-panel.ty-card > .ty-card__body,
@@ -1149,12 +1139,30 @@
.theme-studio__mail-panel-head {
margin-bottom: 12px;
+ flex-wrap: wrap;
+}
+
+.theme-studio__mail-panel-head .ty-input {
+ flex: 1 1 180px;
+ min-width: 0;
}
.theme-studio__mail-message-meta {
display: flex;
align-items: center;
gap: 12px;
+ min-width: 0;
+}
+
+.theme-studio__mail-message-meta > div {
+ min-width: 0;
+}
+
+.theme-studio__mail-message-meta .ty-typography-text {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.theme-studio__mail-nav-item {
@@ -1164,6 +1172,14 @@
gap: 12px;
color: inherit;
text-align: left;
+ overflow: hidden;
+}
+
+.theme-studio__mail-nav-item span {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.theme-studio__mail-nav-item small {
@@ -1208,6 +1224,7 @@
margin-left: auto;
font-size: 12px;
color: var(--editor-muted-foreground);
+ white-space: nowrap;
}
.theme-studio__mail-item-subject {
@@ -1235,6 +1252,12 @@
gap: 12px;
align-items: flex-start;
margin-bottom: 14px;
+ flex-wrap: wrap;
+}
+
+.theme-studio__mail-message-actions {
+ flex-wrap: wrap;
+ justify-content: flex-end;
}
.theme-studio__mail-thread-list {
@@ -1251,6 +1274,7 @@
gap: 8px;
padding-bottom: 12px;
border-bottom: 1px solid var(--editor-border);
+ flex-wrap: wrap;
}
.theme-studio__chat-bubble {
@@ -1264,6 +1288,7 @@
.theme-studio__chat-thread {
gap: 10px;
+ padding: 2px 0 4px;
}
.theme-studio__chat-bubble_in {
@@ -1287,6 +1312,7 @@
.theme-studio__mail-compose-card.ty-card {
padding-top: 0;
border-top: 0;
+ background: color-mix(in srgb, var(--editor-card), var(--editor-base) 10%);
}
.theme-studio__mail-compose-card.ty-card > .ty-card__body {
@@ -1522,7 +1548,7 @@
grid-template-columns: 1fr;
}
- .theme-studio__topbar-copy,
+ .theme-studio__topbar-primary,
.theme-studio__topbar-actions {
align-items: flex-start;
flex-direction: column;
diff --git a/apps/docs/tests/visual/README.md b/apps/docs/tests/visual/README.md
new file mode 100644
index 00000000..3bc40dc2
--- /dev/null
+++ b/apps/docs/tests/visual/README.md
@@ -0,0 +1,19 @@
+# Visual Regression Tests
+
+These Playwright tests cover the highest-risk docs examples for popup positioning,
+component token styling, and dialog overlays.
+
+Run the comparison:
+
+```sh
+pnpm test:visual
+```
+
+Update baselines intentionally:
+
+```sh
+pnpm test:visual:update
+```
+
+The suite targets the docs app and uses the local Chrome channel to avoid storing
+browser binaries in the repository workflow.
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/button-types.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/button-types.png
new file mode 100644
index 00000000..189c36fc
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/button-types.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/drawer-open.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/drawer-open.png
new file mode 100644
index 00000000..1202734f
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/drawer-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/modal-open.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/modal-open.png
new file mode 100644
index 00000000..f62afec2
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/modal-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/table-basic.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/table-basic.png
new file mode 100644
index 00000000..8e129b23
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/table-basic.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/tour-open.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/tour-open.png
new file mode 100644
index 00000000..e44398d4
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/tour-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/transfer-search.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/transfer-search.png
new file mode 100644
index 00000000..cdeb26f0
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/transfer-search.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/upload-list.png b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/upload-list.png
new file mode 100644
index 00000000..d349d2df
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/feedback.visual.spec.ts/upload-list.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-disabled.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-disabled.png
new file mode 100644
index 00000000..ca3d6c66
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-disabled.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-more-types.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-more-types.png
new file mode 100644
index 00000000..abb53a29
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/button-more-types.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-default-value.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-default-value.png
new file mode 100644
index 00000000..e8b9e716
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-default-value.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-disabled-options.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-disabled-options.png
new file mode 100644
index 00000000..08871424
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/cascader-disabled-options.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-disabled.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-disabled.png
new file mode 100644
index 00000000..17f56664
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-disabled.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-extra-footer-open.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-extra-footer-open.png
new file mode 100644
index 00000000..f1298609
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/date-picker-extra-footer-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/dropdown-click-open.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/dropdown-click-open.png
new file mode 100644
index 00000000..0c5e3f72
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/dropdown-click-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/form-size-alignment.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/form-size-alignment.png
new file mode 100644
index 00000000..86d6d15e
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/form-size-alignment.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/pop-confirm-basic-open.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/pop-confirm-basic-open.png
new file mode 100644
index 00000000..556cb861
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/pop-confirm-basic-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/popover-basic-open.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/popover-basic-open.png
new file mode 100644
index 00000000..d0396607
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/popover-basic-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/table-row-selection.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/table-row-selection.png
new file mode 100644
index 00000000..fb2fccfb
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/table-row-selection.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/time-picker-controlled.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/time-picker-controlled.png
new file mode 100644
index 00000000..e7ce3eaa
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/time-picker-controlled.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tooltip-basic-open.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tooltip-basic-open.png
new file mode 100644
index 00000000..67fb4ef8
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tooltip-basic-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tree-selectable.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tree-selectable.png
new file mode 100644
index 00000000..604c906a
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/tree-selectable.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/upload-dragger.png b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/upload-dragger.png
new file mode 100644
index 00000000..0614b360
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/interaction.visual.spec.ts/upload-dragger.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/cascader-change-on-select-open.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/cascader-change-on-select-open.png
new file mode 100644
index 00000000..24407d67
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/cascader-change-on-select-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/color-picker-open.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/color-picker-open.png
new file mode 100644
index 00000000..0e1ffa11
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/color-picker-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/date-picker-range-open.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/date-picker-range-open.png
new file mode 100644
index 00000000..83def81b
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/date-picker-range-open.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-multiple.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-multiple.png
new file mode 100644
index 00000000..cd6908ba
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-multiple.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-search-empty.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-search-empty.png
new file mode 100644
index 00000000..16895fb7
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/select-search-empty.png differ
diff --git a/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/time-picker-open.png b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/time-picker-open.png
new file mode 100644
index 00000000..deca0c4b
Binary files /dev/null and b/apps/docs/tests/visual/__screenshots__/selectors.visual.spec.ts/time-picker-open.png differ
diff --git a/apps/docs/tests/visual/feedback.visual.spec.ts b/apps/docs/tests/visual/feedback.visual.spec.ts
new file mode 100644
index 00000000..134f8cf9
--- /dev/null
+++ b/apps/docs/tests/visual/feedback.visual.spec.ts
@@ -0,0 +1,50 @@
+import { expect, test } from '@playwright/test';
+import { gotoComponent, openFromDemo, previewByTitle, scrollDemoIntoView } from './helpers';
+
+test.describe('feedback and data visuals', () => {
+ test('button types', async ({ page }) => {
+ await gotoComponent(page, 'button');
+ await scrollDemoIntoView(page, 'Type');
+ await expect(previewByTitle(page, 'Type')).toHaveScreenshot('button-types.png');
+ });
+
+ test('modal open state', async ({ page }) => {
+ await gotoComponent(page, 'modal');
+ await openFromDemo(page, 'Basic', 'button');
+ await expect(page.locator('.ty-modal__content')).toBeVisible();
+ await expect(page).toHaveScreenshot('modal-open.png');
+ });
+
+ test('drawer open state', async ({ page }) => {
+ await gotoComponent(page, 'drawer');
+ await openFromDemo(page, 'Basic', 'button');
+ await expect(page.locator('.ty-drawer__content')).toBeVisible();
+ await expect(page).toHaveScreenshot('drawer-open.png');
+ });
+
+ test('tour open state', async ({ page }) => {
+ await gotoComponent(page, 'tour');
+ await openFromDemo(page, 'Basic', 'button:has-text("Start Tour")');
+ await expect(page.locator('.ty-tour')).toBeVisible();
+ await expect(page).toHaveScreenshot('tour-open.png');
+ });
+
+ test('upload list', async ({ page }) => {
+ await gotoComponent(page, 'upload');
+ await scrollDemoIntoView(page, 'Default Files');
+ await expect(previewByTitle(page, 'Default Files')).toHaveScreenshot('upload-list.png');
+ });
+
+ test('table basic', async ({ page }) => {
+ await gotoComponent(page, 'table');
+ await scrollDemoIntoView(page, 'Basic');
+ await expect(previewByTitle(page, 'Basic')).toHaveScreenshot('table-basic.png');
+ });
+
+ test('transfer search', async ({ page }) => {
+ await gotoComponent(page, 'transfer');
+ const demo = await scrollDemoIntoView(page, 'Search');
+ await demo.locator('.ty-input__input').first().fill('1');
+ await expect(previewByTitle(page, 'Search')).toHaveScreenshot('transfer-search.png');
+ });
+});
diff --git a/apps/docs/tests/visual/helpers.ts b/apps/docs/tests/visual/helpers.ts
new file mode 100644
index 00000000..5cc26d87
--- /dev/null
+++ b/apps/docs/tests/visual/helpers.ts
@@ -0,0 +1,48 @@
+import { expect, type Locator, type Page } from '@playwright/test';
+
+export const prepareVisualPage = async (page: Page) => {
+ await page.addStyleTag({
+ content: `
+ *, *::before, *::after {
+ animation-delay: 0s !important;
+ animation-duration: 0s !important;
+ caret-color: transparent !important;
+ scroll-behavior: auto !important;
+ transition-delay: 0s !important;
+ transition-duration: 0s !important;
+ }
+ `,
+ });
+};
+
+export const gotoComponent = async (page: Page, route: string) => {
+ await page.goto(`/components/${route}`);
+ await page.waitForLoadState('networkidle');
+ await prepareVisualPage(page);
+};
+
+export const demoByTitle = (page: Page, title: string): Locator => {
+ return page
+ .locator('.markdown__demo')
+ .filter({ has: page.locator('.markdown__heading-3').filter({ hasText: title }) })
+ .first();
+};
+
+export const previewByTitle = (page: Page, title: string): Locator => {
+ return demoByTitle(page, title).locator('.demo-block__previewer').first();
+};
+
+export const scrollDemoIntoView = async (page: Page, title: string) => {
+ const demo = demoByTitle(page, title);
+ await expect(demo).toBeVisible();
+ await demo.scrollIntoViewIfNeeded();
+ await page.waitForTimeout(100);
+ return demo;
+};
+
+export const openFromDemo = async (page: Page, title: string, selector: string) => {
+ const demo = await scrollDemoIntoView(page, title);
+ await demo.locator(selector).first().click();
+ await page.waitForTimeout(150);
+ return demo;
+};
diff --git a/apps/docs/tests/visual/interaction.visual.spec.ts b/apps/docs/tests/visual/interaction.visual.spec.ts
new file mode 100644
index 00000000..b87009dc
--- /dev/null
+++ b/apps/docs/tests/visual/interaction.visual.spec.ts
@@ -0,0 +1,104 @@
+import { expect, test } from '@playwright/test';
+import { gotoComponent, openFromDemo, previewByTitle, scrollDemoIntoView } from './helpers';
+
+test.describe('popup and interaction visuals', () => {
+ test('button more types', async ({ page }) => {
+ await gotoComponent(page, 'button');
+ await scrollDemoIntoView(page, 'More Types');
+ await expect(previewByTitle(page, 'More Types')).toHaveScreenshot('button-more-types.png');
+ });
+
+ test('button disabled states', async ({ page }) => {
+ await gotoComponent(page, 'button');
+ await scrollDemoIntoView(page, 'Disabled');
+ await expect(previewByTitle(page, 'Disabled')).toHaveScreenshot('button-disabled.png');
+ });
+
+ test('dropdown click menu', async ({ page }) => {
+ await gotoComponent(page, 'dropdown');
+ await scrollDemoIntoView(page, 'Trigger mode');
+ await previewByTitle(page, 'Trigger mode').locator('a').first().click();
+ await expect(page.locator('.ty-dropdown .ty-menu')).toBeVisible();
+ await expect(page).toHaveScreenshot('dropdown-click-open.png');
+ });
+
+ test('tooltip basic hover', async ({ page }) => {
+ await gotoComponent(page, 'tooltip');
+ await scrollDemoIntoView(page, 'Basic');
+ await previewByTitle(page, 'Basic').getByText('Tooltip will show on mouse enter.').hover();
+ await expect(page.locator('.ty-tooltip')).toBeVisible();
+ await expect(page).toHaveScreenshot('tooltip-basic-open.png');
+ });
+
+ test('popover basic hover', async ({ page }) => {
+ await gotoComponent(page, 'popover');
+ const demo = await scrollDemoIntoView(page, 'Basic');
+ await demo.locator('button:has-text("Hover me")').hover();
+ await expect(page.locator('.ty-popover')).toBeVisible();
+ await expect(page).toHaveScreenshot('popover-basic-open.png');
+ });
+
+ test('popconfirm basic open', async ({ page }) => {
+ await gotoComponent(page, 'pop-confirm');
+ await openFromDemo(page, 'Basic', 'button:has-text("Delete")');
+ await expect(page.locator('.ty-pop-confirm')).toBeVisible();
+ await expect(page).toHaveScreenshot('pop-confirm-basic-open.png');
+ });
+
+ test('date picker disabled demo', async ({ page }) => {
+ await gotoComponent(page, 'date-picker');
+ await scrollDemoIntoView(page, 'Disabled');
+ await expect(previewByTitle(page, 'Disabled')).toHaveScreenshot('date-picker-disabled.png');
+ });
+
+ test('date picker extra footer panel', async ({ page }) => {
+ await gotoComponent(page, 'date-picker');
+ await openFromDemo(page, 'Extra Footer', '.ty-date-picker__input');
+ await expect(page.locator('.ty-date-picker__dropdown')).toBeVisible();
+ await expect(page).toHaveScreenshot('date-picker-extra-footer-open.png');
+ });
+
+ test('upload dragger', async ({ page }) => {
+ await gotoComponent(page, 'upload');
+ await scrollDemoIntoView(page, 'Drag and Drop');
+ await expect(previewByTitle(page, 'Drag and Drop')).toHaveScreenshot('upload-dragger.png');
+ });
+
+ test('table row selection checked', async ({ page }) => {
+ await gotoComponent(page, 'table');
+ const demo = await scrollDemoIntoView(page, 'Row Selection');
+ await demo.locator('.ty-table__tbody .ty-checkbox').first().click();
+ await expect(demo.getByText('Selected: 1')).toBeVisible();
+ await expect(previewByTitle(page, 'Row Selection')).toHaveScreenshot('table-row-selection.png');
+ });
+
+ test('tree selectable', async ({ page }) => {
+ await gotoComponent(page, 'tree');
+ await scrollDemoIntoView(page, 'Selectable');
+ await expect(previewByTitle(page, 'Selectable')).toHaveScreenshot('tree-selectable.png');
+ });
+
+ test('form size alignment', async ({ page }) => {
+ await gotoComponent(page, 'form');
+ await scrollDemoIntoView(page, 'Size Alignment');
+ await expect(previewByTitle(page, 'Size Alignment')).toHaveScreenshot('form-size-alignment.png');
+ });
+
+ test('cascader default value', async ({ page }) => {
+ await gotoComponent(page, 'cascader');
+ await scrollDemoIntoView(page, 'Default Value');
+ await expect(previewByTitle(page, 'Default Value')).toHaveScreenshot('cascader-default-value.png');
+ });
+
+ test('cascader disabled options', async ({ page }) => {
+ await gotoComponent(page, 'cascader');
+ await scrollDemoIntoView(page, 'Disabled Options');
+ await expect(previewByTitle(page, 'Disabled Options')).toHaveScreenshot('cascader-disabled-options.png');
+ });
+
+ test('time picker controlled', async ({ page }) => {
+ await gotoComponent(page, 'time-picker');
+ await scrollDemoIntoView(page, 'Controlled');
+ await expect(previewByTitle(page, 'Controlled')).toHaveScreenshot('time-picker-controlled.png');
+ });
+});
diff --git a/apps/docs/tests/visual/selectors.visual.spec.ts b/apps/docs/tests/visual/selectors.visual.spec.ts
new file mode 100644
index 00000000..a6b31c1c
--- /dev/null
+++ b/apps/docs/tests/visual/selectors.visual.spec.ts
@@ -0,0 +1,52 @@
+import { expect, test } from '@playwright/test';
+import { gotoComponent, openFromDemo, previewByTitle, scrollDemoIntoView } from './helpers';
+
+test.describe('selection and picker visuals', () => {
+ test('select search empty dropdown', async ({ page }) => {
+ await gotoComponent(page, 'select');
+ const demo = await openFromDemo(page, 'Search', '.ty-select__selector');
+ await demo.locator('.ty-select__search').fill('zzzz');
+ const empty = page.locator('.ty-select__empty');
+ await expect(empty).toBeVisible();
+ const selectorBox = await demo.locator('.ty-select__selector').boundingBox();
+ const emptyBox = await empty.boundingBox();
+ expect(selectorBox).not.toBeNull();
+ expect(emptyBox).not.toBeNull();
+ expect(emptyBox!.y).toBeGreaterThan(selectorBox!.y + selectorBox!.height - 1);
+ await expect(page).toHaveScreenshot('select-search-empty.png');
+ });
+
+ test('select multiple tags', async ({ page }) => {
+ await gotoComponent(page, 'select');
+ await scrollDemoIntoView(page, 'Multiple');
+ await expect(previewByTitle(page, 'Multiple')).toHaveScreenshot('select-multiple.png');
+ });
+
+ test('cascader change-on-select dropdown', async ({ page }) => {
+ await gotoComponent(page, 'cascader');
+ await openFromDemo(page, 'Change On Select', '.ty-cascader__selector');
+ await expect(page.locator('.ty-cascader__dropdown')).toBeVisible();
+ await expect(page).toHaveScreenshot('cascader-change-on-select-open.png');
+ });
+
+ test('date picker range panel', async ({ page }) => {
+ await gotoComponent(page, 'date-picker');
+ await openFromDemo(page, 'Date Range', '.ty-date-picker__input');
+ await expect(page.locator('.ty-date-picker__dropdown')).toBeVisible();
+ await expect(page).toHaveScreenshot('date-picker-range-open.png');
+ });
+
+ test('time picker panel', async ({ page }) => {
+ await gotoComponent(page, 'time-picker');
+ await openFromDemo(page, 'Controlled', '.ty-time-picker__input');
+ await expect(page.locator('.ty-time-picker__dropdown')).toBeVisible();
+ await expect(page).toHaveScreenshot('time-picker-open.png');
+ });
+
+ test('color picker panel', async ({ page }) => {
+ await gotoComponent(page, 'color-picker');
+ await openFromDemo(page, 'Basic', '.ty-color-picker__trigger');
+ await expect(page.locator('.ty-color-picker__panel')).toBeVisible();
+ await expect(page).toHaveScreenshot('color-picker-open.png');
+ });
+});
diff --git a/apps/pro/.gitignore b/apps/pro/.gitignore
deleted file mode 100644
index 50fc4bdb..00000000
--- a/apps/pro/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.next/
-out/
\ No newline at end of file
diff --git a/apps/pro/next-env.d.ts b/apps/pro/next-env.d.ts
deleted file mode 100644
index 830fb594..00000000
--- a/apps/pro/next-env.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-///
-///
-
-// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/pro/next.config.ts b/apps/pro/next.config.ts
deleted file mode 100644
index f8129d02..00000000
--- a/apps/pro/next.config.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import type { NextConfig } from 'next';
-import path from 'path';
-import { fileURLToPath } from 'url';
-
-const __dirname = path.dirname(fileURLToPath(import.meta.url));
-const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
-
-const nextConfig: NextConfig = {
- output: 'export',
- basePath,
- trailingSlash: true,
- images: { unoptimized: true },
- sassOptions: {
- includePaths: [
- path.resolve(__dirname, 'node_modules'),
- path.resolve(__dirname, '../../node_modules'),
- path.resolve(__dirname, '../../packages/react/src'),
- ],
- },
- webpack(config) {
- // Source alias: resolve to package source (same pattern as docs app)
- config.resolve.alias['@tiny-design/react'] = path.resolve(
- __dirname,
- '../../packages/react/src'
- );
- config.resolve.alias['@tiny-design/icons'] = path.resolve(
- __dirname,
- '../../packages/icons/src'
- );
-
- // ?raw imports: embed original file contents as strings at build time.
- // We must exclude ?raw from existing oneOf rules so Next.js/SWC doesn't
- // compile the TSX before we read it as plain text.
- const rawQuery = /raw/;
-
- for (const rule of config.module.rules) {
- if (rule && typeof rule === 'object' && rule.oneOf) {
- for (const oneOfRule of rule.oneOf) {
- if (oneOfRule && typeof oneOfRule === 'object') {
- // Add resourceQuery exclusion to each sub-rule
- if (!oneOfRule.resourceQuery) {
- oneOfRule.resourceQuery = { not: [rawQuery] };
- } else if (oneOfRule.resourceQuery instanceof RegExp) {
- oneOfRule.resourceQuery = {
- and: [oneOfRule.resourceQuery],
- not: [rawQuery],
- };
- }
- }
- }
- }
- }
-
- config.module.rules.push({
- resourceQuery: rawQuery,
- type: 'asset/source',
- });
-
- return config;
- },
-};
-
-export default nextConfig;
diff --git a/apps/pro/package.json b/apps/pro/package.json
deleted file mode 100644
index 71512579..00000000
--- a/apps/pro/package.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "@tiny-design/pro",
- "version": "0.0.0",
- "private": true,
- "scripts": {
- "dev": "next dev --port 3001",
- "build": "next build",
- "start": "next start"
- },
- "dependencies": {
- "@tiny-design/icons": "workspace:*",
- "@tiny-design/react": "workspace:*",
- "@tiny-design/tokens": "workspace:*",
- "classnames": "^2.5.0",
- "next": "^15.3.4",
- "prism-react-renderer": "^2.3.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-runner": "^1.0.5",
- "sass": "^1.49.9"
- },
- "devDependencies": {
- "@types/node": "^22.0.0",
- "@types/react": "^18.2.0",
- "@types/react-dom": "^18.2.0",
- "typescript": "^5.4.0"
- }
-}
diff --git a/apps/pro/postcss.config.js b/apps/pro/postcss.config.js
deleted file mode 100644
index 9361eff3..00000000
--- a/apps/pro/postcss.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- plugins: {},
-};
diff --git a/apps/pro/src/app/blocks/[category]/category-page-client.tsx b/apps/pro/src/app/blocks/[category]/category-page-client.tsx
deleted file mode 100644
index 9c4dfbcf..00000000
--- a/apps/pro/src/app/blocks/[category]/category-page-client.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-'use client';
-
-import { CategoryNav } from '@/components/layout/category-nav';
-import { BlockPreview } from '@/components/block-preview';
-import { getCategory } from '../../../utils/blocks';
-import styles from './category-page.module.scss';
-
-interface CategoryPageClientProps {
- slug: string;
- label: string;
-}
-
-export function CategoryPageClient({ slug, label }: CategoryPageClientProps) {
- const category = getCategory(slug);
- const blocks = category?.blocks ?? [];
-
- return (
-
-
-
-
-
{label}
-
- {blocks.length} {blocks.length === 1 ? 'block' : 'blocks'}
-
-
- {blocks.map((block) => (
-
- ))}
-
-
- );
-}
diff --git a/apps/pro/src/app/blocks/[category]/category-page.module.scss b/apps/pro/src/app/blocks/[category]/category-page.module.scss
deleted file mode 100644
index 80da192d..00000000
--- a/apps/pro/src/app/blocks/[category]/category-page.module.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-.layout {
- display: flex;
- padding-top: 60px;
- min-height: 100vh;
-}
-
-.content {
- flex: 1;
- margin-left: 240px;
- padding: 32px 40px 80px;
- max-width: calc(100% - 240px);
-}
-
-.pageHeader {
- margin-bottom: 28px;
- padding-bottom: 20px;
- border-bottom: 1px solid var(--ty-color-border);
-}
-
-.title {
- font-size: 30px;
- font-weight: 750;
- letter-spacing: -0.03em;
- margin: 0 0 4px;
- color: var(--ty-color-text);
-}
-
-.count {
- font-size: 12px;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--ty-color-text-tertiary);
- margin: 0;
- font-weight: 700;
-}
diff --git a/apps/pro/src/app/blocks/[category]/page.tsx b/apps/pro/src/app/blocks/[category]/page.tsx
deleted file mode 100644
index a3f19450..00000000
--- a/apps/pro/src/app/blocks/[category]/page.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { notFound } from 'next/navigation';
-import { getCategorySlugs, getCategoryInfo } from '../../../utils/blocks';
-import { CategoryPageClient } from './category-page-client';
-
-export function generateStaticParams() {
- return getCategorySlugs().map((slug) => ({ category: slug }));
-}
-
-interface PageProps {
- params: Promise<{ category: string }>;
-}
-
-export default async function CategoryPage({ params }: PageProps) {
- const { category: slug } = await params;
- const info = getCategoryInfo(slug);
- if (!info) notFound();
- return ;
-}
diff --git a/apps/pro/src/app/globals.scss b/apps/pro/src/app/globals.scss
deleted file mode 100644
index 95bb9c30..00000000
--- a/apps/pro/src/app/globals.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@use '@tiny-design/tokens/scss/base' as *;
-@use 'style/component' as *;
-
-:root {
- --font-heading:
- 'Avenir Next', avenir, montserrat, 'Segoe UI', 'Helvetica Neue', arial, sans-serif;
- --font-body:
- 'Aptos', 'Avenir Next', 'Segoe UI', inter, roboto, 'Helvetica Neue', arial, sans-serif;
-}
-
-*,
-*::before,
-*::after {
- box-sizing: border-box;
-}
-
-body {
- margin: 0;
- padding: 0;
- font-family: var(--font-body), -apple-system, blinkmacsystemfont, 'Segoe UI', roboto,
- 'Helvetica Neue', arial, 'Noto Sans', sans-serif;
- background: var(--ty-color-bg);
- color: var(--ty-color-text);
- transition:
- background-color 0.2s,
- color 0.2s;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
diff --git a/apps/pro/src/app/layout.tsx b/apps/pro/src/app/layout.tsx
deleted file mode 100644
index 549eb9bf..00000000
--- a/apps/pro/src/app/layout.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { Metadata } from 'next';
-import { ThemeScript } from '../components/theme-script';
-import { SiteHeader } from '../components/layout/site-header';
-import './globals.scss';
-
-export const metadata: Metadata = {
- title: 'Tiny Design Pro',
- description: 'Beautiful, ready-to-use UI blocks built with Tiny Design components.',
-};
-
-export default function RootLayout({ children }: { children: React.ReactNode }) {
- return (
-
-
-
-
- {children}
-
-
- );
-}
diff --git a/apps/pro/src/app/page.module.scss b/apps/pro/src/app/page.module.scss
deleted file mode 100644
index 4c9d1bf7..00000000
--- a/apps/pro/src/app/page.module.scss
+++ /dev/null
@@ -1,439 +0,0 @@
-/* ─── Animations ─── */
-@keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(24px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-@keyframes pulseGlow {
- 0%,
- 100% {
- opacity: 0.5;
- }
- 50% {
- opacity: 0.8;
- }
-}
-
-@keyframes dotPulse {
- 0%,
- 100% {
- opacity: 1;
- }
- 50% {
- opacity: 0.4;
- }
-}
-
-/* ─── Layout ─── */
-.main {
- padding-top: 60px;
-}
-
-/* ─── Hero ─── */
-.hero {
- position: relative;
- overflow: hidden;
- padding: 100px 24px 80px;
- display: flex;
- justify-content: center;
- background: var(--ty-color-bg);
-}
-
-.heroGlow {
- position: absolute;
- top: -200px;
- left: 50%;
- transform: translateX(-50%);
- width: 800px;
- height: 600px;
- background: radial-gradient(
- ellipse at center,
- color-mix(in srgb, var(--ty-color-primary) 12%, transparent) 0%,
- transparent 70%
- );
- pointer-events: none;
- animation: pulseGlow 6s ease-in-out infinite;
-}
-
-.heroDots {
- position: absolute;
- inset: 0;
- background-image: radial-gradient(color-mix(in srgb, var(--ty-color-text) 8%, transparent) 1px, transparent 1px);
- background-size: 24px 24px;
- mask-image: radial-gradient(ellipse at 50% 30%, black 20%, transparent 70%);
- -webkit-mask-image: radial-gradient(ellipse at 50% 30%, black 20%, transparent 70%);
- pointer-events: none;
-}
-
-.heroContent {
- position: relative;
- z-index: 1;
- max-width: 720px;
- text-align: center;
- animation: fadeInUp 0.7s ease both;
-}
-
-.badge {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 6px 16px;
- border-radius: 100px;
- font-size: 13px;
- font-weight: 500;
- letter-spacing: 0.01em;
- color: var(--ty-color-text-secondary);
- background: color-mix(in srgb, var(--ty-color-primary) 8%, transparent);
- border: 1px solid color-mix(in srgb, var(--ty-color-primary) 15%, transparent);
- margin-bottom: 28px;
-}
-
-.badgeDot {
- width: 7px;
- height: 7px;
- border-radius: 50%;
- background: var(--ty-color-primary);
- animation: dotPulse 2s ease-in-out infinite;
-}
-
-.title {
- font-family: var(--font-heading), system-ui, sans-serif;
- font-size: clamp(42px, 6.5vw, 72px);
- font-weight: 800;
- line-height: 1.05;
- letter-spacing: -0.04em;
- margin: 0 0 20px;
- color: var(--ty-color-text);
-}
-
-.titleAccent {
- background: linear-gradient(
- 135deg,
- var(--ty-color-primary) 0%,
- color-mix(in srgb, var(--ty-color-primary) 70%, #f59e0b) 50%,
- #f59e0b 100%
- );
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-.subtitle {
- font-size: 18px;
- line-height: 1.7;
- color: var(--ty-color-text-secondary);
- margin: 0 auto 36px;
- max-width: 520px;
-}
-
-.heroActions {
- display: flex;
- gap: 12px;
- justify-content: center;
- flex-wrap: wrap;
-}
-
-/* ─── Buttons ─── */
-.btnPrimary {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 12px 28px;
- border-radius: 10px;
- font-size: 15px;
- font-weight: 600;
- text-decoration: none;
- color: #fff;
- background: var(--ty-color-primary);
- box-shadow: 0 2px 8px color-mix(in srgb, var(--ty-color-primary) 30%, transparent);
- transition: transform 0.2s, box-shadow 0.2s, filter 0.2s;
-
- &:hover {
- transform: translateY(-1px);
- box-shadow: 0 4px 16px color-mix(in srgb, var(--ty-color-primary) 40%, transparent);
- filter: brightness(1.08);
- }
-
- &:active {
- transform: translateY(0);
- }
-}
-
-.btnArrow {
- transition: transform 0.2s;
-
- .btnPrimary:hover & {
- transform: translateX(3px);
- }
-}
-
-.btnSecondary {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 12px 28px;
- border-radius: 10px;
- font-size: 15px;
- font-weight: 600;
- text-decoration: none;
- color: var(--ty-color-text);
- background: transparent;
- border: 1px solid var(--ty-color-border);
- transition: border-color 0.2s, background 0.2s, transform 0.2s;
-
- &:hover {
- background: var(--ty-color-fill-secondary);
- border-color: var(--ty-color-border-secondary);
- transform: translateY(-1px);
- }
-}
-
-/* ─── Stats Ribbon ─── */
-.stats {
- display: flex;
- justify-content: center;
- gap: 60px;
- flex-wrap: wrap;
- padding: 48px 24px;
- border-top: 1px solid var(--ty-color-border-secondary);
- border-bottom: 1px solid var(--ty-color-border-secondary);
- background: color-mix(in srgb, var(--ty-color-primary) 3%, transparent);
-}
-
-.stat {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 4px;
- animation: fadeInUp 0.5s ease both;
-}
-
-.statValue {
- font-family: var(--font-heading), system-ui, sans-serif;
- font-size: 36px;
- font-weight: 700;
- letter-spacing: -0.03em;
- color: var(--ty-color-primary);
-}
-
-.statLabel {
- font-size: 13px;
- font-weight: 500;
- text-transform: uppercase;
- letter-spacing: 0.06em;
- color: var(--ty-color-text-tertiary);
-}
-
-/* ─── Categories Section ─── */
-.categories {
- max-width: 1080px;
- margin: 0 auto;
- padding: 80px 24px;
-}
-
-.sectionHeader {
- text-align: center;
- margin-bottom: 48px;
-}
-
-.sectionTitle {
- font-family: var(--font-heading), system-ui, sans-serif;
- font-size: 32px;
- font-weight: 700;
- letter-spacing: -0.03em;
- margin: 0 0 12px;
- color: var(--ty-color-text);
-}
-
-.sectionDesc {
- font-size: 16px;
- color: var(--ty-color-text-secondary);
- margin: 0;
- line-height: 1.6;
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
- gap: 14px;
-}
-
-.card {
- display: flex;
- align-items: center;
- gap: 16px;
- padding: 18px 20px;
- border-radius: 12px;
- text-decoration: none;
- color: var(--ty-color-text);
- background: var(--ty-color-bg-container);
- border: 1px solid var(--ty-color-border-secondary);
- transition: border-color 0.2s, box-shadow 0.25s, transform 0.2s;
- animation: fadeInUp 0.5s ease both;
-
- &:hover {
- border-color: var(--ty-color-primary);
- transform: translateY(-3px);
- box-shadow:
- 0 4px 12px color-mix(in srgb, var(--ty-color-primary) 8%, transparent),
- 0 12px 32px rgba(0, 0, 0, 0.06);
-
- .cardArrow {
- opacity: 1;
- transform: translateX(0);
- }
- }
-}
-
-.cardIcon {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 44px;
- height: 44px;
- border-radius: 11px;
- font-size: 20px;
- flex-shrink: 0;
- background: color-mix(in srgb, var(--ty-color-primary) 8%, transparent);
-}
-
-.cardTitle {
- margin: 0 0 2px;
- font-size: 15px;
- font-weight: 600;
- letter-spacing: -0.01em;
-}
-
-.cardCount {
- margin: 0;
- font-size: 13px;
- color: var(--ty-color-text-tertiary);
-}
-
-.cardArrow {
- margin-left: auto;
- font-size: 16px;
- color: var(--ty-color-primary);
- opacity: 0;
- transform: translateX(-6px);
- transition: opacity 0.2s, transform 0.2s;
-}
-
-/* ─── CTA Section ─── */
-.cta {
- position: relative;
- text-align: center;
- padding: 80px 24px;
- border-top: 1px solid var(--ty-color-border-secondary);
- background: color-mix(in srgb, var(--ty-color-primary) 3%, transparent);
- overflow: hidden;
-}
-
-.ctaTitle {
- font-family: var(--font-heading), system-ui, sans-serif;
- font-size: clamp(26px, 4vw, 36px);
- font-weight: 700;
- letter-spacing: -0.03em;
- margin: 0 0 12px;
- color: var(--ty-color-text);
-}
-
-.ctaDesc {
- font-size: 16px;
- color: var(--ty-color-text-secondary);
- line-height: 1.7;
- margin: 0 auto 28px;
- max-width: 480px;
-}
-
-.ctaActions {
- display: flex;
- justify-content: center;
-}
-
-/* ─── Footer ─── */
-.footer {
- padding: 32px 24px;
- text-align: center;
- border-top: 1px solid var(--ty-color-border-secondary);
-
- p {
- margin: 0;
- font-size: 13px;
- color: var(--ty-color-text-tertiary);
- }
-
- a {
- color: var(--ty-color-primary);
- text-decoration: none;
- font-weight: 500;
-
- &:hover {
- text-decoration: underline;
- }
- }
-}
-
-/* ─── Responsive ─── */
-@media (max-width: 768px) {
- .hero {
- padding: 70px 20px 60px;
- }
-
- .subtitle {
- font-size: 16px;
- }
-
- .stats {
- gap: 32px 48px;
- padding: 36px 20px;
- }
-
- .statValue {
- font-size: 28px;
- }
-
- .categories {
- padding: 56px 20px;
- }
-
- .grid {
- grid-template-columns: 1fr;
- }
-
- .cta {
- padding: 56px 20px;
- }
-}
-
-@media (max-width: 480px) {
- .hero {
- padding: 56px 16px 48px;
- }
-
- .heroActions {
- flex-direction: column;
- align-items: center;
- }
-
- .btnPrimary,
- .btnSecondary {
- width: 100%;
- max-width: 280px;
- justify-content: center;
- }
-
- .stats {
- gap: 24px 36px;
- }
-
- .statValue {
- font-size: 24px;
- }
-}
diff --git a/apps/pro/src/app/page.tsx b/apps/pro/src/app/page.tsx
deleted file mode 100644
index 146474c5..00000000
--- a/apps/pro/src/app/page.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import Link from 'next/link';
-import { getCategories } from '../utils/blocks';
-import styles from './page.module.scss';
-
-const CATEGORY_ICONS: Record = {
- authentication: '🔐',
- banners: '📢',
- cards: '🃏',
- 'form-layouts': '📝',
- lists: '📋',
- navbars: '🧭',
- notifications: '🔔',
- 'page-headers': '📄',
- 'page-shells': '🏗',
- 'progress-steps': '📊',
- sidebars: '📑',
- stats: '📈',
- tables: '🗃',
- 'user-cards': '👤',
-};
-
-export default function HomePage() {
- const categories = getCategories();
- const totalBlocks = categories.reduce((sum, cat) => sum + cat.blocks.length, 0);
-
- return (
-
- {/* Hero section */}
-
-
-
-
-
-
- Open Source · Copy & Paste · Free
-
-
- Production-Ready
-
- UI Blocks
-
-
- Beautifully crafted, ready-to-use interface blocks built entirely with
- Tiny Design components. Browse, preview, and copy into your projects.
-
-
-
-
-
- {/* Stats ribbon */}
-
- {[
- { value: String(totalBlocks), label: 'UI Blocks' },
- { value: String(categories.length), label: 'Categories' },
- { value: '80+', label: 'Components Used' },
- { value: '100%', label: 'TypeScript' },
- ].map((stat, i) => (
-
- {stat.value}
- {stat.label}
-
- ))}
-
-
- {/* Categories section */}
-
-
-
Explore by Category
-
- Each block is a self-contained, composable UI pattern you can drop into any React project.
-
-
-
- {categories.map((cat, i) => (
-
-
- {CATEGORY_ICONS[cat.slug] || '📦'}
-
-
-
{cat.label}
-
- {cat.blocks.length} {cat.blocks.length === 1 ? 'block' : 'blocks'}
-
-
-
→
-
- ))}
-
-
-
- {/* CTA section */}
-
- Start Building Beautiful Interfaces
-
- Every block uses only Tiny Design components — no extra dependencies.
- Just copy, paste, and customize.
-
-
-
- Get Started
- →
-
-
-
-
- {/* Footer */}
-
-
- );
-}
diff --git a/apps/pro/src/blocks/authentication/sign-in-simple.tsx b/apps/pro/src/blocks/authentication/sign-in-simple.tsx
deleted file mode 100644
index eb7dc2bb..00000000
--- a/apps/pro/src/blocks/authentication/sign-in-simple.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Button, Card, Checkbox, Divider, Flex, Form, Input, InputPassword, Typography } from '@tiny-design/react';
-import { IconLock, IconGoogle, IconGithub } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-export default function SignInSimple() {
- return (
-
-
-
-
-
-
-
- Welcome back
-
- Sign in to access your team workspace
-
-
-
-
-
-
-
- Google
-
-
-
-
-
- GitHub
-
-
-
-
-
or continue with email
-
-
-
-
-
-
-
-
- Remember me
- Forgot password?
-
-
- Sign in
-
-
-
-
- Don't have an account? Create one free
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/authentication/sign-up-simple.tsx b/apps/pro/src/blocks/authentication/sign-up-simple.tsx
deleted file mode 100644
index 4549e813..00000000
--- a/apps/pro/src/blocks/authentication/sign-up-simple.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Button, Card, Checkbox, Flex, Form, Input, InputPassword, Progress, Typography } from '@tiny-design/react';
-import { IconAddUser } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-export default function SignUpSimple() {
- return (
-
-
-
-
-
-
-
- Create your account
-
- Start your 14-day trial with a clean setup flow
-
-
-
-
-
-
-
-
-
-
-
- Password strength
- Strong
-
-
-
-
-
- I agree to the Terms of Service and Privacy Policy
-
-
-
- Get started free
-
-
-
-
- Already have an account? Sign in
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/banners/alert-banner.tsx b/apps/pro/src/blocks/banners/alert-banner.tsx
deleted file mode 100644
index ef12be5d..00000000
--- a/apps/pro/src/blocks/banners/alert-banner.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import { Alert, Flex, Tag, Typography } from '@tiny-design/react';
-import {
- IconCheckCircle,
- IconCalendar,
- IconWarning,
- IconCreditCard,
- IconArrowRight,
-} from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-const iconBox = (gradient: string, shadow: string): React.CSSProperties => ({
- width: 36,
- height: 36,
- borderRadius: 10,
- marginRight: 12,
- background: gradient,
- display: 'inline-flex',
- alignItems: 'center',
- justifyContent: 'center',
- boxShadow: `0 3px 10px ${shadow}`,
- flexShrink: 0,
-});
-
-const linkStyle = (color: string): React.CSSProperties => ({
- color,
- fontWeight: 600,
- fontSize: 13,
- cursor: 'pointer',
- display: 'inline-flex',
- alignItems: 'center',
- gap: 3,
- textDecoration: 'none',
-});
-
-export default function AlertBanner() {
- return (
-
-
-
-
- }
- title={
-
- Deployment successful
-
- Live
-
-
- }
- >
-
-
- All 24 checks passed — your app is now live at production.
-
-
- View logs
-
-
-
-
-
-
-
- }
- title={
- Scheduled maintenance
- }
- >
-
-
- Systems will be briefly unavailable on March 30, 2:00–4:00 AM UTC.
-
-
- View details
-
-
-
-
-
-
-
- }
- title={
-
- Usage limit approaching
-
- 89%
-
-
- }
- >
-
-
- You've consumed 89% of your monthly API quota. Upgrade to avoid interruption.
-
-
- Upgrade plan
-
-
-
-
-
-
-
- }
- title={
-
- Payment failed
-
- Action required
-
-
- }
- >
-
-
- We couldn't charge your Visa ending in 4242. Please update your billing info.
-
-
- Update card
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/banners/promo-banner.tsx b/apps/pro/src/blocks/banners/promo-banner.tsx
deleted file mode 100644
index 30a5a4ad..00000000
--- a/apps/pro/src/blocks/banners/promo-banner.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Button, Flex, Tag, Typography } from '@tiny-design/react';
-import { IconFire } from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-export default function PromoBanner() {
- return (
-
-
-
-
-
-
-
- New
-
-
-
- Tiny Design v2.0 is here — redesigned components, dark mode, and 40+ new blocks.
-
-
- Explore now
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/cards/profile-card.tsx b/apps/pro/src/blocks/cards/profile-card.tsx
deleted file mode 100644
index f81409d7..00000000
--- a/apps/pro/src/blocks/cards/profile-card.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { Avatar, Button, Card, Divider, Flex, Tag, Typography } from '@tiny-design/react';
-import { IconBriefcase } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-export default function ProfileCard() {
- return (
-
-
-
-
-
-
-
- JD
-
-
- Jane Doe
-
-
- Software Engineer
-
-
-
- Building beautiful UIs with Tiny Design. Open-source enthusiast and design systems advocate.
-
-
- React
- TypeScript
- Design Systems
-
-
-
-
-
-
-
- 128
- Projects
-
-
- 1.2k
- Followers
-
-
- 384
- Following
-
-
-
-
-
- Follow
-
-
- Message
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/cards/stats-card.tsx b/apps/pro/src/blocks/cards/stats-card.tsx
deleted file mode 100644
index 7b0d84af..00000000
--- a/apps/pro/src/blocks/cards/stats-card.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Card, Flex, Statistic, Tag, Typography } from '@tiny-design/react';
-import { IconWallet, IconTeam, IconPieChart, IconBullish } from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-const stats = [
- { title: 'Total Revenue', value: 45231.89, prefix: '$', change: '+20.1%', up: true, icon: IconWallet, color: '#1d4ed8', bg: '#eff6ff' },
- { title: 'Subscribers', value: 2350, change: '+180', up: true, icon: IconTeam, color: '#0f766e', bg: '#ecfeff' },
- { title: 'Conversion', value: 12.5, suffix: '%', change: '+4.3%', up: true, icon: IconBullish, color: '#15803d', bg: '#f0fdf4' },
- { title: 'Bounce Rate', value: 24.5, suffix: '%', change: '-2.1%', up: false, icon: IconPieChart, color: '#b45309', bg: '#fffbeb' },
-];
-
-export default function StatsCard() {
- return (
-
-
- {stats.map((s) => {
- const Icon = s.icon;
- return (
-
-
-
-
-
- {s.title}
-
-
-
-
-
-
-
-
-
-
- {s.change}
-
- vs last month
-
-
-
-
- );
- })}
-
-
- );
-}
diff --git a/apps/pro/src/blocks/form-layouts/contact-form.tsx b/apps/pro/src/blocks/form-layouts/contact-form.tsx
deleted file mode 100644
index f5fc2ebb..00000000
--- a/apps/pro/src/blocks/form-layouts/contact-form.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Button, Card, Flex, Form, Input, NativeSelect, Textarea, Typography } from '@tiny-design/react';
-import { IconComment, IconCustomerSupport } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-export default function ContactForm() {
- return (
-
-
-
-
-
-
-
- Get in touch
-
- Have a question or need help? Fill out the form below and our team will get back to you within 24 hours.
-
-
-
-
-
-
-
-
-
-
- Select a topic...
- General Inquiry
- Technical Support
- Billing Question
- Enterprise Sales
-
-
-
-
-
-
-
- Cancel
-
-
-
-
- Send Message
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/lists/user-list.tsx b/apps/pro/src/blocks/lists/user-list.tsx
deleted file mode 100644
index 9c18e716..00000000
--- a/apps/pro/src/blocks/lists/user-list.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Avatar, Button, Card, Flex, Input, List, Tag, Typography } from '@tiny-design/react';
-import { IconSearch, IconAddUser } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-const users = [
- { name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin', status: 'Active', avatar: 'AJ', color: '#6366f1' },
- { name: 'Bob Smith', email: 'bob@example.com', role: 'Editor', status: 'Active', avatar: 'BS', color: '#0891b2' },
- { name: 'Carol White', email: 'carol@example.com', role: 'Viewer', status: 'Inactive', avatar: 'CW', color: '#64748b' },
- { name: 'David Brown', email: 'david@example.com', role: 'Editor', status: 'Active', avatar: 'DB', color: '#059669' },
- { name: 'Eva Martinez', email: 'eva@example.com', role: 'Admin', status: 'Active', avatar: 'EM', color: '#e11d48' },
-];
-
-const roleColors: Record = { Admin: 'purple', Editor: 'blue', Viewer: 'default' };
-
-export default function UserList() {
- return (
-
-
-
-
-
- Team Members
-
- Manage your team and their permissions
-
-
-
- }
- size="sm"
- style={{ width: 200, borderRadius: 12 }}
- />
-
-
-
- Invite
-
-
-
-
-
-
(
- Edit,
- Remove ,
- ]}
- >
-
-
- {user.avatar}
-
-
- {user.name}
-
- {user.email}
-
-
- {user.role}
-
-
-
- )}
- />
-
-
-
- Showing 5 of 5 members
-
-
- 3 Admin · 2 Editor · 1 Viewer
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/navbars/navbar-simple.tsx b/apps/pro/src/blocks/navbars/navbar-simple.tsx
deleted file mode 100644
index cd2e9252..00000000
--- a/apps/pro/src/blocks/navbars/navbar-simple.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { Avatar, Badge, Button, Flex, Menu, Typography } from '@tiny-design/react';
-import { IconFeedback, IconSetting } from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-export default function NavbarSimple() {
- return (
-
-
-
-
-
- A
-
- Acme Inc
-
-
- Dashboard
- Projects
- Team
- Reports
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- JD
-
-
- Jane Doe
- Admin
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/navbars/navbar-with-search.tsx b/apps/pro/src/blocks/navbars/navbar-with-search.tsx
deleted file mode 100644
index 29ff6c31..00000000
--- a/apps/pro/src/blocks/navbars/navbar-with-search.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { Avatar, Button, Flex, Input, Keyboard, Menu, Tag, Typography } from '@tiny-design/react';
-import { IconSearch, IconBroadcast } from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-export default function NavbarWithSearch() {
- return (
-
-
-
-
-
- W
-
- Workspace
-
- Pro
-
-
-
- Home
- Docs
- API
- Changelog
-
-
-
- }
- suffix={/ }
- size="sm"
- style={{ width: 220, borderRadius: 12, background: '#fbfcfe' }}
- />
-
-
-
- Upgrade
-
-
-
- U
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/notifications/notification-list.tsx b/apps/pro/src/blocks/notifications/notification-list.tsx
deleted file mode 100644
index a960080d..00000000
--- a/apps/pro/src/blocks/notifications/notification-list.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import { Badge, Button, Card, Flex, List, Typography } from '@tiny-design/react';
-import { IconComment, IconCheckCircle, IconTeam, IconStatistics } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-const notifications = [
- {
- id: 1,
- title: 'New comment on your pull request',
- description: 'Alice reviewed: "Looks great! Just one small suggestion on the auth handler."',
- time: '5 min ago',
- read: false,
- avatar: 'AJ',
- color: '#6366f1',
- icon: IconComment,
- },
- {
- id: 2,
- title: 'Deployment succeeded',
- description: 'tiny-design@2.1.0 deployed to production — all health checks passing.',
- time: '1 hour ago',
- read: false,
- avatar: 'CI',
- color: '#059669',
- icon: IconCheckCircle,
- },
- {
- id: 3,
- title: 'Bob Smith joined Engineering',
- description: 'New team member added to the engineering team. Say hello!',
- time: '3 hours ago',
- read: true,
- avatar: 'BS',
- color: '#0891b2',
- icon: IconTeam,
- },
- {
- id: 4,
- title: 'Weekly analytics report',
- description: 'Your weekly report is ready — downloads up 23% this week.',
- time: 'Yesterday',
- read: true,
- avatar: 'WR',
- color: '#e11d48',
- icon: IconStatistics,
- },
-];
-
-export default function NotificationList() {
- return (
-
-
-
-
-
- Notifications
-
-
- Mark all read
-
-
- You have 2 unread notifications
-
-
-
-
-
(
-
-
-
-
-
-
- {!item.read && (
-
- )}
-
-
-
- {item.title}
-
- {item.time}
-
-
-
- {item.description}
-
-
-
-
- )}
- />
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/page-headers/page-header-with-breadcrumb.tsx b/apps/pro/src/blocks/page-headers/page-header-with-breadcrumb.tsx
deleted file mode 100644
index 731bf320..00000000
--- a/apps/pro/src/blocks/page-headers/page-header-with-breadcrumb.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import { Avatar, Breadcrumb, Button, Divider, Flex, Tag, Typography } from '@tiny-design/react';
-import { IconSetting, IconBranch, IconCheckCircle } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-export default function PageHeaderWithBreadcrumb() {
- return (
-
-
- Home
- Projects
- Tiny Design
-
-
-
-
-
- T
-
-
-
- Tiny Design
-
-
-
- Active
-
-
-
-
-
- A friendly UI component set for React
-
-
-
- main
-
-
-
-
-
- {['JD', 'AJ', 'BS'].map((initials, i) => (
- 0 ? -8 : 0,
- }}
- >
- {initials}
-
- ))}
-
- +5
-
-
-
- 8 contributors
-
-
-
-
-
-
-
-
-
- Settings
-
-
-
- Deploy
-
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/page-shells/dashboard-shell.tsx b/apps/pro/src/blocks/page-shells/dashboard-shell.tsx
deleted file mode 100644
index 85064b60..00000000
--- a/apps/pro/src/blocks/page-shells/dashboard-shell.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { Avatar, Button, Card, Divider, Flex, Layout, Menu, Statistic, Tag, Typography } from '@tiny-design/react';
-import { IconTeam, IconSearch, IconWallet, IconBullish } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-const { Header, Sidebar, Content } = Layout;
-
-export default function DashboardShell() {
- return (
-
-
-
-
- A
-
-
- Acme Inc
- Enterprise
-
-
-
-
-
- Main
-
-
- Overview
- Analytics
- Reports
-
-
- Manage
-
-
- Users
- Settings
-
-
-
-
- JD
-
- Jane Doe
- Admin
-
-
-
-
-
-
- Overview
-
-
-
-
-
- Create Report
-
-
-
-
-
- {[
- { title: 'Total Users', value: 12834, icon: IconTeam, color: '#1d4ed8', bg: '#eff6ff', change: '+12%' },
- { title: 'Revenue', value: 45231, prefix: '$', icon: IconWallet, color: '#15803d', bg: '#f0fdf4', change: '+8.2%' },
- { title: 'Growth', value: 23.5, suffix: '%', icon: IconBullish, color: '#0f766e', bg: '#ecfeff', change: '+4.1%' },
- ].map((s) => (
-
-
-
-
- {s.title}
-
-
-
-
-
-
-
{s.change}
-
-
- ))}
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/progress-steps/steps-basic.tsx b/apps/pro/src/blocks/progress-steps/steps-basic.tsx
deleted file mode 100644
index 9fba339b..00000000
--- a/apps/pro/src/blocks/progress-steps/steps-basic.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { Button, Card, Divider, Flex, Steps, Tag, Typography } from '@tiny-design/react';
-
-const { Heading, Text } = Typography;
-
-export default function StepsBasic() {
- return (
-
-
-
-
-
-
- Create New Project
- Step 2 of 4
-
-
- Configure your project settings and preferences
-
-
-
-
-
-
-
-
-
-
-
-
-
- Configuration
-
- Choose the framework, deployment target, and build options for your project.
-
-
- {['React', 'Vercel', 'TypeScript'].map((item) => (
- {item}
- ))}
-
-
-
-
-
-
-
-
- Previous
-
-
- Save Draft
-
- Next Step
-
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/sidebars/sidebar-with-groups.tsx b/apps/pro/src/blocks/sidebars/sidebar-with-groups.tsx
deleted file mode 100644
index aa20e122..00000000
--- a/apps/pro/src/blocks/sidebars/sidebar-with-groups.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Avatar, Divider, Flex, Menu, Progress, Tag, Typography } from '@tiny-design/react';
-
-const { Text } = Typography;
-
-export default function SidebarWithGroups() {
- return (
-
- {/* Organization header */}
-
-
- A
-
-
- Acme Inc
-
- Pro Plan
-
-
-
-
-
-
- {/* Navigation groups */}
-
-
- General
-
-
- Dashboard
- Projects
- Tasks
- Analytics
-
-
-
- Settings
-
-
- General
- Members
- Billing
-
-
-
- {/* Usage footer */}
-
-
- Storage used
- 7.2 / 10 GB
-
-
-
-
- {/* User footer */}
-
-
- JD
-
- Jane Doe
- jane@acme.co
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/stats/stat-with-icon.tsx b/apps/pro/src/blocks/stats/stat-with-icon.tsx
deleted file mode 100644
index 9878720e..00000000
--- a/apps/pro/src/blocks/stats/stat-with-icon.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Card, Flex, Progress, Statistic, Tag, Typography } from '@tiny-design/react';
-import { IconWallet, IconTeam, IconStarFill, IconBullish } from '@tiny-design/icons';
-
-const { Text } = Typography;
-
-const metrics = [
- { title: 'Total Sales', value: 89420, prefix: '$', progress: 72, trend: '+12.5%', icon: IconWallet, color: '#6366f1', bg: '#eef2ff' },
- { title: 'New Customers', value: 1423, progress: 58, trend: '+8.2%', icon: IconTeam, color: '#0891b2', bg: '#ecfeff' },
- { title: 'Satisfaction', value: 94.2, suffix: '%', progress: 94, trend: '+2.1%', icon: IconStarFill, color: '#f59e0b', bg: '#fffbeb' },
- { title: 'Growth Rate', value: 23.5, suffix: '%', progress: 85, trend: '+4.3%', icon: IconBullish, color: '#059669', bg: '#ecfdf5' },
-];
-
-export default function StatWithIcon() {
- return (
-
-
- {metrics.map((m) => {
- const Icon = m.icon;
- return (
-
-
-
-
-
-
-
-
- {m.title}
-
-
-
-
-
-
-
- Target progress
- {m.progress}%
-
-
-
-
-
- {m.trend}
- vs last period
-
-
-
- );
- })}
-
-
- );
-}
diff --git a/apps/pro/src/blocks/tables/data-table.tsx b/apps/pro/src/blocks/tables/data-table.tsx
deleted file mode 100644
index 14fe4dfe..00000000
--- a/apps/pro/src/blocks/tables/data-table.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import { Avatar, Button, Card, Flex, Input, Table, Tag, Typography } from '@tiny-design/react';
-import { IconSearch, IconPlus, IconDownload } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-const data = [
- { key: '1', name: 'Alice Johnson', email: 'alice@acme.co', role: 'Admin', status: 'Active', lastLogin: '2 hours ago', color: '#6366f1' },
- { key: '2', name: 'Bob Smith', email: 'bob@acme.co', role: 'Engineer', status: 'Active', lastLogin: '1 day ago', color: '#0891b2' },
- { key: '3', name: 'Carol White', email: 'carol@acme.co', role: 'Designer', status: 'Inactive', lastLogin: '2 weeks ago', color: '#64748b' },
- { key: '4', name: 'David Brown', email: 'david@acme.co', role: 'Engineer', status: 'Active', lastLogin: '5 hours ago', color: '#059669' },
- { key: '5', name: 'Eva Martinez', email: 'eva@acme.co', role: 'Admin', status: 'Active', lastLogin: '30 min ago', color: '#e11d48' },
-];
-
-const roleColors: Record = { Admin: 'purple', Engineer: 'blue', Designer: 'cyan', Viewer: 'default' };
-
-const columns = [
- {
- title: 'Member',
- dataIndex: 'name',
- key: 'name',
- render: (_: unknown, record: (typeof data)[0]) => (
-
-
- {record.name.split(' ').map((n) => n[0]).join('')}
-
-
- {record.name}
-
- {record.email}
-
-
- ),
- },
- {
- title: 'Role',
- dataIndex: 'role',
- key: 'role',
- render: (role: string) => (
- {role}
- ),
- },
- {
- title: 'Status',
- dataIndex: 'status',
- key: 'status',
- render: (status: string) => (
-
-
- {status}
-
- ),
- },
- {
- title: 'Last Active',
- dataIndex: 'lastLogin',
- key: 'lastLogin',
- render: (val: string) => (
- {val}
- ),
- },
- {
- title: '',
- key: 'action',
- render: () => (
-
- Edit
- Remove
-
- ),
- },
-];
-
-export default function DataTable() {
- return (
-
-
-
-
-
- Team Members
-
- Manage and review all team members
-
-
-
- }
- size="sm"
- style={{ width: 180, borderRadius: 12, background: '#fbfcfe' }}
- />
-
-
-
- Export
-
-
-
-
-
- Add Member
-
-
-
-
-
-
-
- Showing 5 results
-
-
- 2 Admin
- 2 Engineer
- 1 Designer
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/blocks/user-cards/user-card-simple.tsx b/apps/pro/src/blocks/user-cards/user-card-simple.tsx
deleted file mode 100644
index 303ce3b4..00000000
--- a/apps/pro/src/blocks/user-cards/user-card-simple.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { Avatar, Button, Card, Flex, Tag, Typography } from '@tiny-design/react';
-import { IconLink, IconComment } from '@tiny-design/icons';
-
-const { Heading, Text } = Typography;
-
-const users = [
- {
- name: 'Alice Johnson',
- role: 'Engineering Lead',
- avatar: 'AJ',
- color: '#6366f1',
- gradient: 'linear-gradient(135deg, #6366f1 0%, #a855f7 100%)',
- tags: ['React', 'TypeScript', 'Node.js'],
- projects: 42,
- contributions: '1.2k',
- },
- {
- name: 'Bob Smith',
- role: 'Product Designer',
- avatar: 'BS',
- color: '#0891b2',
- gradient: 'linear-gradient(135deg, #0891b2 0%, #06b6d4 100%)',
- tags: ['Figma', 'UI/UX', 'Motion'],
- projects: 38,
- contributions: '890',
- },
- {
- name: 'Carol White',
- role: 'Backend Engineer',
- avatar: 'CW',
- color: '#059669',
- gradient: 'linear-gradient(135deg, #059669 0%, #10b981 100%)',
- tags: ['Go', 'PostgreSQL', 'K8s'],
- projects: 56,
- contributions: '2.1k',
- },
-];
-
-export default function UserCardSimple() {
- return (
-
- {users.map((user) => (
-
-
-
-
- {user.avatar}
-
-
- {user.name}
- {user.role}
-
-
- {user.tags.map((tag) => (
- {tag}
- ))}
-
-
-
- {user.projects}
- Projects
-
-
- {user.contributions}
- Commits
-
-
-
-
- Connect
-
-
-
-
- Message
-
-
-
-
-
- ))}
-
- );
-}
diff --git a/apps/pro/src/components/block-preview/block-preview.module.scss b/apps/pro/src/components/block-preview/block-preview.module.scss
deleted file mode 100644
index f6822763..00000000
--- a/apps/pro/src/components/block-preview/block-preview.module.scss
+++ /dev/null
@@ -1,280 +0,0 @@
-// ── Block Preview Card ──────────────────────────────────────────────
-.blockPreview {
- border: 1px solid var(--ty-color-border);
- border-radius: 16px;
- overflow: hidden;
- background: var(--ty-color-bg-container);
- box-shadow:
- 0 1px 2px rgba(15, 23, 42, 0.03),
- 0 12px 32px rgba(15, 23, 42, 0.05);
- transition:
- border-color 0.25s,
- background-color 0.25s,
- box-shadow 0.25s;
-
- & + & {
- margin-top: 40px;
- }
-
- &:hover {
- border-color: color-mix(in srgb, var(--ty-color-primary) 30%, var(--ty-color-border));
- box-shadow:
- 0 4px 12px rgba(15, 23, 42, 0.06),
- 0 16px 40px rgba(15, 23, 42, 0.08);
- }
-}
-
-// ── Header ─────────────────────────────────────────────────────────
-.header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid var(--ty-color-border);
- background: color-mix(in srgb, var(--ty-color-bg-elevated) 92%, #f8fafc);
- transition:
- background-color 0.25s,
- border-color 0.25s;
-}
-
-.title {
- margin: 0;
- font-size: 15px;
- font-weight: 650;
- letter-spacing: -0.015em;
- color: var(--ty-color-text);
-}
-
-// ── Toolbar ────────────────────────────────────────────────────────
-.toolbar {
- display: flex;
- align-items: center;
- gap: 6px;
-}
-
-// Segmented viewport switcher
-.viewportGroup {
- display: flex;
- align-items: center;
- background: var(--ty-color-bg-container);
- border: 1px solid var(--ty-color-border);
- border-radius: 10px;
- padding: 3px;
- gap: 2px;
-}
-
-.viewportBtn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 30px;
- height: 28px;
- border: none;
- border-radius: 6px;
- background: transparent;
- color: var(--ty-color-text-tertiary);
- cursor: pointer;
- font-size: 15px;
- transition:
- background-color 0.2s,
- color 0.2s,
- box-shadow 0.2s;
-
- &:hover {
- color: var(--ty-color-text-secondary);
- }
-}
-
-.viewportBtnActive {
- background: color-mix(in srgb, var(--ty-color-primary) 10%, var(--ty-color-bg-container));
- color: var(--ty-color-text);
- box-shadow:
- inset 0 0 0 1px color-mix(in srgb, var(--ty-color-primary) 22%, transparent);
-}
-
-// Divider between viewport and actions
-.toolbarDivider {
- width: 1px;
- height: 20px;
- background: var(--ty-color-border-secondary);
- margin: 0 4px;
-}
-
-// Action buttons
-.actionGroup {
- display: flex;
- align-items: center;
- gap: 2px;
-}
-
-.actionBtn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 30px;
- height: 30px;
- border: none;
- border-radius: 6px;
- background: transparent;
- color: var(--ty-color-text-tertiary);
- cursor: pointer;
- font-size: 15px;
- transition:
- background-color 0.15s,
- color 0.15s;
-
- &:hover {
- color: var(--ty-color-text);
- background: var(--ty-color-fill-secondary);
- }
-}
-
-.actionBtnActive {
- color: var(--ty-color-primary);
- background: var(--ty-color-primary-bg);
-
- &:hover {
- color: var(--ty-color-primary);
- background: var(--ty-color-primary-bg);
- }
-}
-
-// ── Preview Frame ──────────────────────────────────────────────────
-.previewOuter {
- position: relative;
- overflow: hidden;
- min-height: 180px;
- padding: 24px;
- background:
- linear-gradient(180deg, color-mix(in srgb, var(--ty-color-bg) 94%, #f8fafc) 0%, var(--ty-color-bg) 100%);
- transition: background-color 0.3s;
-}
-
-.previewOuterScaled {
- display: flex;
- justify-content: center;
- align-items: flex-start;
- background-image:
- linear-gradient(color-mix(in srgb, var(--ty-color-border-secondary) 45%, transparent) 1px, transparent 1px),
- linear-gradient(90deg, color-mix(in srgb, var(--ty-color-border-secondary) 45%, transparent) 1px, transparent 1px),
- linear-gradient(180deg, color-mix(in srgb, var(--ty-color-bg) 94%, #f8fafc) 0%, var(--ty-color-bg) 100%);
- background-size: 24px 24px, 24px 24px, auto;
- background-position: center;
-}
-
-.previewInner {
- width: 100%;
-}
-
-.previewInnerScaled {
- margin: 0 auto;
- box-shadow:
- 0 0 0 1px var(--ty-color-border),
- 0 16px 36px rgba(15, 23, 42, 0.08);
- background: var(--ty-color-bg-container);
- border-radius: 16px;
- overflow: hidden;
-}
-
-// ── Action Bar (between preview and code) ──────────────────────────
-.actionBar {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 6px;
- padding: 10px 16px;
- border-top: 1px solid var(--ty-color-border);
- background: color-mix(in srgb, var(--ty-color-bg-elevated) 92%, #f8fafc);
- transition:
- background-color 0.25s,
- border-color 0.25s;
-}
-
-.actionBarBtn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- height: 28px;
- border: none;
- border-radius: 6px;
- background: transparent;
- color: var(--ty-color-text-tertiary);
- cursor: pointer;
- font-size: 13px;
- padding: 0 8px;
- gap: 5px;
- transition:
- background-color 0.15s,
- color 0.15s;
-
- &:hover {
- color: var(--ty-color-text);
- background: var(--ty-color-fill-secondary);
- }
-
- svg {
- width: 14px;
- height: 14px;
- }
-}
-
-.actionBarBtnActive {
- color: var(--ty-color-primary);
- background: color-mix(in srgb, var(--ty-color-primary) 10%, transparent);
-
- &:hover {
- color: var(--ty-color-primary);
- }
-}
-
-.actionBarDivider {
- width: 1px;
- height: 16px;
- background: var(--ty-color-border-secondary);
- margin: 0 2px;
-}
-
-.actionBarLabel {
- font-family: inherit;
- font-size: 12px;
- font-weight: 500;
-}
-
-// ── Code Panel ─────────────────────────────────────────────────────
-.codePanelWrapper {
- display: grid;
- grid-template-rows: 0fr;
- transition: grid-template-rows 0.3s ease;
- border-top: 1px solid var(--ty-color-border);
-}
-
-.codePanelWrapperOpen {
- grid-template-rows: 1fr;
-}
-
-.codePanelInner {
- overflow: hidden;
-}
-
-.codePanel {
- max-height: 480px;
- overflow: auto;
- font-size: 13px;
- line-height: 1.7;
-
- pre {
- margin: 0;
- }
-}
-
-// ── Error ──────────────────────────────────────────────────────────
-.errorDisplay {
- padding: 16px 20px;
- font-size: 13px;
- font-family: 'Menlo', 'Consolas', 'Droid Sans Mono', monospace;
- line-height: 1.5;
- color: var(--ty-color-danger);
- background: var(--ty-color-danger-bg);
- white-space: pre-wrap;
- word-break: break-word;
-}
diff --git a/apps/pro/src/components/block-preview/code-panel.tsx b/apps/pro/src/components/block-preview/code-panel.tsx
deleted file mode 100644
index 3fde8e4d..00000000
--- a/apps/pro/src/components/block-preview/code-panel.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-'use client';
-
-import { Highlight, themes } from 'prism-react-renderer';
-import { useTheme } from '@tiny-design/react';
-import { LightCodeTheme, DarkCodeTheme } from './code-theme';
-import styles from './block-preview.module.scss';
-
-interface CodePanelProps {
- source: string;
-}
-
-export function CodePanel({ source }: CodePanelProps) {
- const { resolvedTheme } = useTheme();
- const theme = (resolvedTheme === 'dark' ? DarkCodeTheme : LightCodeTheme) as typeof themes.github;
-
- return (
-
-
- {({ className, style, tokens, getLineProps, getTokenProps }) => (
-
-
- {tokens.map((line, i) => {
- const { key: _lineKey, ...lineProps } = getLineProps({ line }); // eslint-disable-line @typescript-eslint/no-unused-vars
- return (
-
- {line.map((token, j) => {
- const { key: _tokenKey, ...tokenProps } = getTokenProps({ token }); // eslint-disable-line @typescript-eslint/no-unused-vars
- return ;
- })}
-
- );
- })}
-
-
- )}
-
-
- );
-}
diff --git a/apps/pro/src/components/block-preview/code-theme.ts b/apps/pro/src/components/block-preview/code-theme.ts
deleted file mode 100644
index 01987e32..00000000
--- a/apps/pro/src/components/block-preview/code-theme.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-/* Light theme: based on prism-ghcolors */
-export const LightCodeTheme = {
- plain: {
- color: '#393A34',
- backgroundColor: '#f6f8fa',
- },
- styles: [
- {
- types: ['comment', 'prolog', 'doctype', 'cdata'],
- style: { color: '#999988', fontStyle: 'italic' as const },
- },
- {
- types: ['namespace'],
- style: { opacity: 0.7 },
- },
- {
- types: ['string', 'attr-name'],
- style: { color: '#0b8235' },
- },
- {
- types: ['punctuation', 'operator'],
- style: { color: '#999' },
- },
- {
- types: [
- 'entity', 'url', 'symbol', 'number', 'boolean',
- 'variable', 'constant', 'property', 'regex', 'inserted',
- ],
- style: { color: '#36acaa' },
- },
- {
- types: ['atrule', 'keyword', 'selector', 'attr-value'],
- style: { color: '#00a4db' },
- },
- {
- types: ['function', 'deleted', 'tag', 'function-variable', 'at-rule', 'class-name'],
- style: { color: '#f81d22' },
- },
- {
- types: ['function-variable'],
- style: { color: '#6f42c1' },
- },
- {
- types: ['tag', 'selector', 'keyword'],
- style: { color: '#6f42c1' },
- },
- ],
-};
-
-/* Dark theme: based on VS Code Dark+ */
-export const DarkCodeTheme = {
- plain: {
- color: '#d4d4d4',
- backgroundColor: '#1e1e1e',
- },
- styles: [
- {
- types: ['comment', 'prolog', 'doctype', 'cdata'],
- style: { color: '#6a9955', fontStyle: 'italic' as const },
- },
- {
- types: ['namespace'],
- style: { opacity: 0.7 },
- },
- {
- types: ['string', 'attr-name'],
- style: { color: '#ce9178' },
- },
- {
- types: ['punctuation', 'operator'],
- style: { color: '#808080' },
- },
- {
- types: [
- 'entity', 'url', 'symbol', 'number', 'boolean',
- 'variable', 'constant', 'property', 'regex', 'inserted',
- ],
- style: { color: '#b5cea8' },
- },
- {
- types: ['atrule', 'keyword', 'selector', 'attr-value'],
- style: { color: '#569cd6' },
- },
- {
- types: ['function', 'deleted', 'tag', 'function-variable', 'at-rule', 'class-name'],
- style: { color: '#d7ba7d' },
- },
- {
- types: ['function-variable'],
- style: { color: '#dcdcaa' },
- },
- {
- types: ['tag', 'selector', 'keyword'],
- style: { color: '#c586c0' },
- },
- ],
-};
diff --git a/apps/pro/src/components/block-preview/index.tsx b/apps/pro/src/components/block-preview/index.tsx
deleted file mode 100644
index 7aba29a4..00000000
--- a/apps/pro/src/components/block-preview/index.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-'use client';
-
-import React, { useState, useEffect, useCallback } from 'react';
-import { useRunner } from 'react-runner';
-import * as TinyDesign from '@tiny-design/react';
-import * as TinyIcons from '@tiny-design/icons';
-import { Toolbar, CodeIcon, CopyIcon, CheckIcon, type ViewportSize } from './toolbar';
-import { PreviewFrame } from './preview-frame';
-import { CodePanel } from './code-panel';
-import type { BlockMeta } from '../../utils/blocks';
-import styles from './block-preview.module.scss';
-
-// In dev mode, Next.js appends HMR code (import.meta.webpackHot...) to
-// asset/source modules. Strip it so react-runner only sees the TSX source.
-function stripHmr(raw: string): string {
- const marker = '\n\n;\n // Wrapped in an IIFE';
- const idx = raw.indexOf(marker);
- return idx !== -1 ? raw.substring(0, idx).trim() : raw.trim();
-}
-
-const scope = {
- import: {
- react: React,
- '@tiny-design/react': TinyDesign,
- '@tiny-design/icons': TinyIcons,
- },
-};
-
-interface BlockPreviewProps {
- meta: BlockMeta;
-}
-
-export function BlockPreview({ meta }: BlockPreviewProps) {
- const [viewport, setViewport] = useState('desktop');
- const [showCode, setShowCode] = useState(false);
- const [sourceCode, setSourceCode] = useState('');
- const [copied, setCopied] = useState(false);
-
- useEffect(() => {
- meta.rawSource().then((m) => setSourceCode(stripHmr(m.default)));
- }, [meta]);
-
- const { element, error } = useRunner({ code: sourceCode, scope });
-
- const handleCopy = useCallback(async () => {
- await navigator.clipboard.writeText(sourceCode);
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
- }, [sourceCode]);
-
- return (
-
-
-
{meta.title}
-
-
-
-
- {error ? (
- {error}
- ) : (
- element
- )}
-
-
- {/* Docs-inspired action bar */}
-
- setShowCode((v) => !v)}
- >
-
-
- {showCode ? 'Hide Code' : 'Show Code'}
-
-
-
-
- {copied ? : }
-
- {copied ? 'Copied!' : 'Copy'}
-
-
-
-
- {/* Animated code panel */}
-
-
- );
-}
diff --git a/apps/pro/src/components/block-preview/preview-frame.tsx b/apps/pro/src/components/block-preview/preview-frame.tsx
deleted file mode 100644
index 755e8723..00000000
--- a/apps/pro/src/components/block-preview/preview-frame.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-'use client';
-
-import { useRef, useState, useEffect } from 'react';
-import type { ViewportSize } from './toolbar';
-import styles from './block-preview.module.scss';
-
-const VIEWPORT_WIDTHS: Record = {
- desktop: 0, // 0 = fluid / 100%
- tablet: 768,
- mobile: 375,
-};
-
-interface PreviewFrameProps {
- viewport: ViewportSize;
- children: React.ReactNode;
-}
-
-export function PreviewFrame({ viewport, children }: PreviewFrameProps) {
- const outerRef = useRef(null);
- const innerRef = useRef(null);
- const [scale, setScale] = useState(1);
- const [innerHeight, setInnerHeight] = useState(undefined);
- const targetWidth = VIEWPORT_WIDTHS[viewport];
-
- useEffect(() => {
- if (!outerRef.current || targetWidth === 0) {
- setScale(1);
- setInnerHeight(undefined);
- return;
- }
-
- const observer = new ResizeObserver(() => {
- if (!outerRef.current) return;
- const availableWidth = outerRef.current.clientWidth;
- const newScale = Math.min(1, availableWidth / targetWidth);
- setScale(newScale);
-
- if (innerRef.current) {
- setInnerHeight(innerRef.current.scrollHeight * newScale + 48);
- }
- });
-
- observer.observe(outerRef.current);
- if (innerRef.current) {
- observer.observe(innerRef.current);
- }
-
- return () => observer.disconnect();
- }, [viewport, targetWidth]);
-
- const isScaled = targetWidth > 0;
-
- return (
-
- );
-}
diff --git a/apps/pro/src/components/block-preview/toolbar.tsx b/apps/pro/src/components/block-preview/toolbar.tsx
deleted file mode 100644
index 72c785e8..00000000
--- a/apps/pro/src/components/block-preview/toolbar.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-'use client';
-
-import styles from './block-preview.module.scss';
-
-export type ViewportSize = 'desktop' | 'tablet' | 'mobile';
-
-const DesktopIcon = () => (
-
-
-
-
-
-);
-
-const TabletIcon = () => (
-
-
-
-
-);
-
-const MobileIcon = () => (
-
-
-
-
-);
-
-export const CodeIcon = () => (
-
-
-
-
-);
-
-export const CopyIcon = () => (
-
-
-
-
-);
-
-export const CheckIcon = () => (
-
-
-
-);
-
-interface ToolbarProps {
- viewport: ViewportSize;
- onViewportChange: (v: ViewportSize) => void;
-}
-
-export function Toolbar({ viewport, onViewportChange }: ToolbarProps) {
- return (
-
-
- {([
- ['desktop', DesktopIcon],
- ['tablet', TabletIcon],
- ['mobile', MobileIcon],
- ] as const).map(([size, Icon]) => (
- onViewportChange(size)}
- aria-label={`${size} view`}
- >
-
-
- ))}
-
-
- );
-}
diff --git a/apps/pro/src/components/layout/category-nav.module.scss b/apps/pro/src/components/layout/category-nav.module.scss
deleted file mode 100644
index f5cac0dc..00000000
--- a/apps/pro/src/components/layout/category-nav.module.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-.nav {
- position: fixed;
- top: 60px;
- left: 0;
- bottom: 0;
- width: 240px;
- overflow-y: auto;
- padding: 24px 0;
- background: color-mix(in srgb, var(--ty-color-bg-elevated) 94%, #f8fafc);
- border-right: 1px solid var(--ty-color-border);
- transition:
- background-color 0.25s,
- border-color 0.25s;
-}
-
-.title {
- padding: 6px 24px 12px;
- font-size: 11px;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--ty-color-text-tertiary);
-}
-
-.link {
- display: block;
- margin: 2px 12px;
- padding: 10px 12px;
- font-size: 13px;
- font-weight: 500;
- color: var(--ty-color-text-secondary);
- text-decoration: none;
- border: 1px solid transparent;
- border-radius: 10px;
- transition:
- color 0.15s,
- background-color 0.15s,
- border-color 0.15s;
-
- &:hover {
- color: var(--ty-color-text);
- background: var(--ty-color-fill-secondary);
- border-color: color-mix(in srgb, var(--ty-color-border) 80%, transparent);
- }
-
- &.active {
- color: var(--ty-color-primary);
- font-weight: 600;
- border-color: color-mix(in srgb, var(--ty-color-primary) 20%, transparent);
- background: color-mix(in srgb, var(--ty-color-primary) 10%, transparent);
- }
-}
diff --git a/apps/pro/src/components/layout/category-nav.tsx b/apps/pro/src/components/layout/category-nav.tsx
deleted file mode 100644
index c5b119ea..00000000
--- a/apps/pro/src/components/layout/category-nav.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-'use client';
-
-import Link from 'next/link';
-import { usePathname } from 'next/navigation';
-import { CATEGORIES } from '../../utils/blocks';
-import styles from './category-nav.module.scss';
-
-export function CategoryNav() {
- const pathname = usePathname();
-
- return (
-
- Application
- {CATEGORIES.map((cat) => {
- const href = `/blocks/${cat.slug}/`;
- const isActive = pathname?.includes(`/blocks/${cat.slug}`);
- return (
-
- {cat.label}
-
- );
- })}
-
- );
-}
diff --git a/apps/pro/src/components/layout/site-header.module.scss b/apps/pro/src/components/layout/site-header.module.scss
deleted file mode 100644
index ecc80a13..00000000
--- a/apps/pro/src/components/layout/site-header.module.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-.header {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- height: 60px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 24px;
- background: color-mix(in srgb, var(--ty-color-bg-container) 85%, transparent);
- backdrop-filter: blur(12px) saturate(180%);
- -webkit-backdrop-filter: blur(12px) saturate(180%);
- border-bottom: 1px solid var(--ty-color-border-secondary);
- transition:
- background-color 0.25s,
- border-color 0.25s;
-}
-
-.logo {
- display: flex;
- align-items: center;
- gap: 10px;
- text-decoration: none;
- color: var(--ty-color-text);
- font-weight: 700;
- font-size: 16px;
- letter-spacing: -0.02em;
-}
-
-.actions {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.iconBtn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 34px;
- height: 34px;
- border: 1px solid var(--ty-color-border-secondary);
- border-radius: 8px;
- background: transparent;
- color: var(--ty-color-text-secondary);
- cursor: pointer;
- font-size: 16px;
- transition:
- background-color 0.15s,
- color 0.15s,
- border-color 0.15s;
-
- &:hover {
- color: var(--ty-color-text);
- background: var(--ty-color-fill-secondary);
- border-color: var(--ty-color-border);
- }
-}
diff --git a/apps/pro/src/components/layout/site-header.tsx b/apps/pro/src/components/layout/site-header.tsx
deleted file mode 100644
index 31a0d74c..00000000
--- a/apps/pro/src/components/layout/site-header.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import Link from 'next/link';
-import { useTheme } from '@tiny-design/react';
-import styles from './site-header.module.scss';
-
-const SunIcon = () => (
-
-
-
-
-
-
-
-
-
-
-
-);
-
-const MoonIcon = () => (
-
-
-
-);
-
-const GitHubIcon = () => (
-
-
-
-);
-
-export function SiteHeader() {
- const { resolvedTheme, toggle } = useTheme();
- const [mounted, setMounted] = useState(false);
-
- useEffect(() => setMounted(true), []);
-
- return (
-
-
- Tiny Design Pro
-
-
-
- {mounted ? (resolvedTheme === 'dark' ? : ) : }
-
-
-
-
-
-
- );
-}
diff --git a/apps/pro/src/components/theme-script/index.tsx b/apps/pro/src/components/theme-script/index.tsx
deleted file mode 100644
index 037e691f..00000000
--- a/apps/pro/src/components/theme-script/index.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-export function ThemeScript() {
- const script = `
- (function() {
- try {
- var t = localStorage.getItem('ty-theme') || 'light';
- document.documentElement.setAttribute('data-tiny-theme', t);
- } catch(e) {}
- })();
- `;
- return ;
-}
diff --git a/apps/pro/src/env.d.ts b/apps/pro/src/env.d.ts
deleted file mode 100644
index ddb8910d..00000000
--- a/apps/pro/src/env.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-declare module '*?raw' {
- const content: string;
- export default content;
-}
-
-declare module '*.module.scss' {
- const classes: { [key: string]: string };
- export default classes;
-}
diff --git a/apps/pro/src/utils/blocks.ts b/apps/pro/src/utils/blocks.ts
deleted file mode 100644
index e3248203..00000000
--- a/apps/pro/src/utils/blocks.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-export interface BlockMeta {
- id: string;
- title: string;
- category: string;
- categoryLabel: string;
- rawSource: () => Promise<{ default: string }>;
-}
-
-export interface CategoryMeta {
- slug: string;
- label: string;
- blocks: BlockMeta[];
-}
-
-function block(
- category: string,
- categoryLabel: string,
- id: string,
- title: string,
- rawSource: () => Promise<{ default: string }>
-): BlockMeta {
- return { id: `${category}/${id}`, title, category, categoryLabel, rawSource };
-}
-
-export const CATEGORIES: CategoryMeta[] = [
- {
- slug: 'authentication',
- label: 'Authentication',
- blocks: [
- block('authentication', 'Authentication', 'sign-in-simple', 'Simple Sign In',
- () => import('../blocks/authentication/sign-in-simple?raw')),
- block('authentication', 'Authentication', 'sign-up-simple', 'Simple Sign Up',
- () => import('../blocks/authentication/sign-up-simple?raw')),
- ],
- },
- {
- slug: 'banners',
- label: 'Banners',
- blocks: [
- block('banners', 'Banners', 'promo-banner', 'Promo Banner',
- () => import('../blocks/banners/promo-banner?raw')),
- block('banners', 'Banners', 'alert-banner', 'Alert Banner',
- () => import('../blocks/banners/alert-banner?raw')),
- ],
- },
- {
- slug: 'cards',
- label: 'Cards',
- blocks: [
- block('cards', 'Cards', 'stats-card', 'Stats Cards',
- () => import('../blocks/cards/stats-card?raw')),
- block('cards', 'Cards', 'profile-card', 'Profile Card',
- () => import('../blocks/cards/profile-card?raw')),
- ],
- },
- {
- slug: 'form-layouts',
- label: 'Form Layouts',
- blocks: [
- block('form-layouts', 'Form Layouts', 'contact-form', 'Contact Form',
- () => import('../blocks/form-layouts/contact-form?raw')),
- ],
- },
- {
- slug: 'lists',
- label: 'Lists',
- blocks: [
- block('lists', 'Lists', 'user-list', 'User List',
- () => import('../blocks/lists/user-list?raw')),
- ],
- },
- {
- slug: 'navbars',
- label: 'Navbars',
- blocks: [
- block('navbars', 'Navbars', 'navbar-simple', 'Simple Navbar',
- () => import('../blocks/navbars/navbar-simple?raw')),
- block('navbars', 'Navbars', 'navbar-with-search', 'Navbar with Search',
- () => import('../blocks/navbars/navbar-with-search?raw')),
- ],
- },
- {
- slug: 'notifications',
- label: 'Notifications',
- blocks: [
- block('notifications', 'Notifications', 'notification-list', 'Notification List',
- () => import('../blocks/notifications/notification-list?raw')),
- ],
- },
- {
- slug: 'page-headers',
- label: 'Page Headers',
- blocks: [
- block('page-headers', 'Page Headers', 'page-header-with-breadcrumb', 'Page Header with Breadcrumb',
- () => import('../blocks/page-headers/page-header-with-breadcrumb?raw')),
- ],
- },
- {
- slug: 'page-shells',
- label: 'Page Shells',
- blocks: [
- block('page-shells', 'Page Shells', 'dashboard-shell', 'Dashboard Shell',
- () => import('../blocks/page-shells/dashboard-shell?raw')),
- ],
- },
- {
- slug: 'progress-steps',
- label: 'Progress Steps',
- blocks: [
- block('progress-steps', 'Progress Steps', 'steps-basic', 'Basic Steps',
- () => import('../blocks/progress-steps/steps-basic?raw')),
- ],
- },
- {
- slug: 'sidebars',
- label: 'Sidebars',
- blocks: [
- block('sidebars', 'Sidebars', 'sidebar-with-groups', 'Sidebar with Groups',
- () => import('../blocks/sidebars/sidebar-with-groups?raw')),
- ],
- },
- {
- slug: 'stats',
- label: 'Stats',
- blocks: [
- block('stats', 'Stats', 'stat-with-icon', 'Stats with Progress',
- () => import('../blocks/stats/stat-with-icon?raw')),
- ],
- },
- {
- slug: 'tables',
- label: 'Tables',
- blocks: [
- block('tables', 'Tables', 'data-table', 'Data Table',
- () => import('../blocks/tables/data-table?raw')),
- ],
- },
- {
- slug: 'user-cards',
- label: 'User Cards',
- blocks: [
- block('user-cards', 'User Cards', 'user-card-simple', 'Simple User Cards',
- () => import('../blocks/user-cards/user-card-simple?raw')),
- ],
- },
-];
-
-export function getCategories(): CategoryMeta[] {
- return CATEGORIES;
-}
-
-export function getCategory(slug: string): CategoryMeta | undefined {
- return CATEGORIES.find((c) => c.slug === slug);
-}
-
-export function getCategorySlugs(): string[] {
- return CATEGORIES.map((c) => c.slug);
-}
-
-export function getCategoryInfo(slug: string): { label: string; blockCount: number } | undefined {
- const cat = CATEGORIES.find((c) => c.slug === slug);
- if (!cat) return undefined;
- return { label: cat.label, blockCount: cat.blocks.length };
-}
diff --git a/apps/pro/tsconfig.json b/apps/pro/tsconfig.json
deleted file mode 100644
index 68c950c4..00000000
--- a/apps/pro/tsconfig.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "baseUrl": ".",
- "module": "esnext",
- "noUnusedLocals": false,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": [
- "./src/*"
- ],
- "@tiny-design/react": [
- "../../packages/react/src"
- ],
- "@tiny-design/react/*": [
- "../../packages/react/src/*"
- ],
- "@tiny-design/icons": [
- "../../packages/icons/src"
- ],
- "@tiny-design/icons/*": [
- "../../packages/icons/src/*"
- ]
- },
- "skipLibCheck": true,
- "incremental": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve"
- },
- "include": [
- "next-env.d.ts",
- "src",
- ".next/types/**/*.ts"
- ],
- "exclude": [
- "node_modules"
- ]
-}
diff --git a/package.json b/package.json
index 12d018c9..0767032b 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,8 @@
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
+ "test:visual": "playwright test -c apps/docs/playwright.config.ts",
+ "test:visual:update": "playwright test -c apps/docs/playwright.config.ts --update-snapshots",
"lint": "turbo run lint",
"lint:style": "turbo run lint:style",
"prepare": "husky",
@@ -29,6 +31,7 @@
"@changesets/cli": "^2.30.0",
"@changesets/get-github-info": "^0.8.0",
"@eslint/js": "^9.0.0",
+ "@playwright/test": "^1.59.1",
"eslint": "^9.0.0",
"eslint-plugin-jest": "^28.0.0",
"eslint-plugin-jest-dom": "^5.0.0",
@@ -45,7 +48,7 @@
"typescript-eslint": "^8.0.0"
},
"lint-staged": {
- "packages/{react/src,tokens/scss}/**/*.scss": [
+ "packages/react/src/**/*.scss": [
"stylelint --fix"
]
},
diff --git a/packages/cli/scripts/extract.ts b/packages/cli/scripts/extract.ts
index 55a91003..406f5ec3 100644
--- a/packages/cli/scripts/extract.ts
+++ b/packages/cli/scripts/extract.ts
@@ -8,7 +8,8 @@ const __dirname = path.dirname(__filename);
const DATA_DIR = path.resolve(__dirname, '../src/data');
const REACT_SRC = path.resolve(__dirname, '../../react/src');
-const VARIABLES_PATH = path.resolve(__dirname, '../../tokens/scss/_variables.scss');
+const TOKEN_REGISTRY_PATH = path.resolve(__dirname, '../../tokens/dist/registry.json');
+const VARIABLES_PATH = path.resolve(__dirname, '../../react/src/style/_variables.scss');
const ICONS_INDEX = path.resolve(__dirname, '../../icons/src/index.ts');
function ensureDir(dir: string) {
@@ -33,7 +34,7 @@ writeJson(
);
console.log(' extracting tokens...');
-writeJson('tokens.json', extractTokens({ variablesPath: VARIABLES_PATH }));
+writeJson('tokens.json', extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH }));
console.log(' extracting icons...');
writeJson('icons.json', extractIcons({ iconsIndexPath: ICONS_INDEX }));
diff --git a/packages/extract/src/extract-tokens.ts b/packages/extract/src/extract-tokens.ts
index 83adfcbb..c6c5fad0 100644
--- a/packages/extract/src/extract-tokens.ts
+++ b/packages/extract/src/extract-tokens.ts
@@ -1,5 +1,4 @@
import * as fs from 'node:fs';
-import * as path from 'node:path';
import type { TokenData, ExtractTokensOptions } from './types.js';
// Map variable name prefixes to categories
@@ -33,6 +32,27 @@ export function extractTokens(options: ExtractTokensOptions): TokenData {
shadows: {},
};
+ if (options.registryPath) {
+ const registry = JSON.parse(fs.readFileSync(options.registryPath, 'utf-8')) as {
+ tokens?: Array<{ key: string; cssVar: string; type: string; defaultValue: string | number }>;
+ };
+
+ for (const token of registry.tokens ?? []) {
+ const category = categorize(token.key);
+
+ if (category) {
+ result[category][token.key] = {
+ variable: token.cssVar,
+ value: String(token.defaultValue),
+ };
+ }
+ }
+ }
+
+ if (!options.variablesPath) {
+ return result;
+ }
+
const variablesContent = fs.readFileSync(options.variablesPath, 'utf-8');
// Parse SCSS variable declarations: $name: value !default;
@@ -45,37 +65,12 @@ export function extractTokens(options: ExtractTokensOptions): TokenData {
const category = categorize(name);
if (category) {
- result[category][name] = {
+ result[category][name] ??= {
variable: `$${name}`,
value,
};
}
}
- // Also parse theme map files in the themes/ directory next to _variables.scss
- const themesDir = path.join(path.dirname(options.variablesPath), 'themes');
- const lightThemePath = path.join(themesDir, '_light.scss');
-
- if (fs.existsSync(lightThemePath)) {
- const themeContent = fs.readFileSync(lightThemePath, 'utf-8');
-
- // Match map entries: key: value,
- // Handles multi-value entries like shadows by matching up to the trailing comma
- const mapEntryRegex = /^\s+([a-z0-9-]+):\s*(.+?),?\s*$/gm;
-
- while ((match = mapEntryRegex.exec(themeContent)) !== null) {
- const name = match[1];
- const value = match[2].replace(/,\s*$/, '');
- const category = categorize(name);
-
- if (category && !result[category][name]) {
- result[category][name] = {
- variable: `--ty-${name}`,
- value,
- };
- }
- }
- }
-
return result;
}
diff --git a/packages/extract/src/types.ts b/packages/extract/src/types.ts
index ccf21836..bf38f89b 100644
--- a/packages/extract/src/types.ts
+++ b/packages/extract/src/types.ts
@@ -54,8 +54,10 @@ export interface ExtractComponentsOptions {
}
export interface ExtractTokensOptions {
- /** Path to the tokens SCSS variables file */
- variablesPath: string;
+ /** Path to the v2 token registry JSON file */
+ registryPath?: string;
+ /** Optional path to React SCSS compile-time variables for breakpoints */
+ variablesPath?: string;
}
export interface ExtractIconsOptions {
diff --git a/packages/mcp/README.md b/packages/mcp/README.md
index 24914908..900d3e10 100644
--- a/packages/mcp/README.md
+++ b/packages/mcp/README.md
@@ -55,7 +55,7 @@ Add to your MCP client config:
| `list_components` | List all 80+ components. Filter by category: Foundation, Layout, Navigation, Data Display, Form, Feedback, Miscellany. |
| `get_component_props` | Get the full props interface for a component — types, required flags, descriptions. |
| `get_component_example` | Get usage examples (demo code) for a component. |
-| `get_design_tokens` | Get design tokens (SCSS variables) — colors, typography, spacing, breakpoints, shadows. |
+| `get_design_tokens` | Get v2 design tokens from the token registry — colors, typography, spacing, breakpoints, shadows. |
| `list_icons` | List all 240+ icon names. Filter by search term. |
| `get_icon` | Get details and usage example for a specific icon. |
diff --git a/packages/mcp/__tests__/extract-tokens.test.ts b/packages/mcp/__tests__/extract-tokens.test.ts
index df698cd4..67cad2f7 100644
--- a/packages/mcp/__tests__/extract-tokens.test.ts
+++ b/packages/mcp/__tests__/extract-tokens.test.ts
@@ -3,11 +3,12 @@ import { fileURLToPath } from 'node:url';
import { extractTokens } from '@tiny-design/extract';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
-const VARIABLES_PATH = path.resolve(__dirname, '../../tokens/scss/_variables.scss');
+const TOKEN_REGISTRY_PATH = path.resolve(__dirname, '../../tokens/dist/registry.json');
+const VARIABLES_PATH = path.resolve(__dirname, '../../react/src/style/_variables.scss');
describe('extractTokens', () => {
it('extracts tokens grouped by category', () => {
- const result = extractTokens({ variablesPath: VARIABLES_PATH });
+ const result = extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH });
// Should have known categories
expect(Object.keys(result)).toEqual(
@@ -16,7 +17,7 @@ describe('extractTokens', () => {
});
it('extracts color tokens', () => {
- const result = extractTokens({ variablesPath: VARIABLES_PATH });
+ const result = extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH });
expect(result.colors['color-primary']).toEqual({
variable: '--ty-color-primary',
@@ -30,16 +31,16 @@ describe('extractTokens', () => {
});
it('extracts typography tokens', () => {
- const result = extractTokens({ variablesPath: VARIABLES_PATH });
+ const result = extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH });
expect(result.typography['font-size-base']).toEqual({
variable: '--ty-font-size-base',
- value: '1rem',
+ value: '14px',
});
});
it('extracts breakpoint tokens', () => {
- const result = extractTokens({ variablesPath: VARIABLES_PATH });
+ const result = extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH });
expect(result.breakpoints['size-xs']).toEqual({
variable: '$size-xs',
@@ -48,7 +49,7 @@ describe('extractTokens', () => {
});
it('extracts shadow tokens', () => {
- const result = extractTokens({ variablesPath: VARIABLES_PATH });
+ const result = extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH });
expect(result.shadows).toBeDefined();
expect(result.shadows['shadow-sm']).toBeDefined();
diff --git a/packages/mcp/scripts/extract.ts b/packages/mcp/scripts/extract.ts
index f22332cc..d17e8ff6 100644
--- a/packages/mcp/scripts/extract.ts
+++ b/packages/mcp/scripts/extract.ts
@@ -8,7 +8,8 @@ const __dirname = path.dirname(__filename);
const DATA_DIR = path.resolve(__dirname, '../src/data');
const REACT_SRC = path.resolve(__dirname, '../../react/src');
-const VARIABLES_PATH = path.resolve(__dirname, '../../tokens/scss/_variables.scss');
+const TOKEN_REGISTRY_PATH = path.resolve(__dirname, '../../tokens/dist/registry.json');
+const VARIABLES_PATH = path.resolve(__dirname, '../../react/src/style/_variables.scss');
const ICONS_INDEX = path.resolve(__dirname, '../../icons/src/index.ts');
function ensureDir(dir: string) {
@@ -30,7 +31,7 @@ console.log(' extracting components...');
writeJson('components.json', extractComponents({ reactSrcPath: REACT_SRC }));
console.log(' extracting tokens...');
-writeJson('tokens.json', extractTokens({ variablesPath: VARIABLES_PATH }));
+writeJson('tokens.json', extractTokens({ registryPath: TOKEN_REGISTRY_PATH, variablesPath: VARIABLES_PATH }));
console.log(' extracting icons...');
writeJson('icons.json', extractIcons({ iconsIndexPath: ICONS_INDEX }));
diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts
index fde468d4..fe5825fb 100644
--- a/packages/mcp/src/index.ts
+++ b/packages/mcp/src/index.ts
@@ -56,7 +56,7 @@ server.tool(
server.tool(
'get_design_tokens',
- 'Get Tiny Design design tokens (SCSS variables). Categories: colors, typography, spacing, breakpoints, shadows.',
+ 'Get Tiny Design v2 design tokens from the token registry. Categories: colors, typography, spacing, breakpoints, shadows.',
{ category: z.string().optional().describe('Token category to filter by') },
async ({ category }) => ({
content: [{ type: 'text' as const, text: JSON.stringify(getDesignTokens(category), null, 2) }],
diff --git a/packages/react/README.md b/packages/react/README.md
index 03afe029..391cebdb 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -15,7 +15,6 @@ pnpm add @tiny-design/react
## Quick Start
```tsx
-import '@tiny-design/tokens';
import { Button, ConfigProvider } from '@tiny-design/react';
function App() {
diff --git a/packages/react/scripts/build-styles.js b/packages/react/scripts/build-styles.js
index 906f4cd7..8e564b32 100644
--- a/packages/react/scripts/build-styles.js
+++ b/packages/react/scripts/build-styles.js
@@ -19,7 +19,7 @@ async function processWithPostcss(css) {
return result.css;
}
-// 1. Base CSS: copy the v2 runtime theme bundle from @tiny-design/tokens
+// 1. Base CSS: copy the runtime theme bundle from @tiny-design/tokens
function copyBaseCss() {
const src = require.resolve('@tiny-design/tokens/dist/css/base.css');
for (const dir of [ES_DIR, LIB_DIR]) {
@@ -27,7 +27,7 @@ function copyBaseCss() {
mkdirp(outDir);
fs.copyFileSync(src, path.join(outDir, 'base.css'));
}
- console.log(' es/style/base.css + lib/style/base.css (copied from @tiny-design/tokens v2 base theme CSS)');
+ console.log(' es/style/base.css + lib/style/base.css (copied from @tiny-design/tokens base theme CSS)');
}
// 2. Per-component CSS: compile each component's style/index.scss entry
diff --git a/packages/react/src/alert/style/index.scss b/packages/react/src/alert/style/index.scss
index 0a145935..dc0853e2 100755
--- a/packages/react/src/alert/style/index.scss
+++ b/packages/react/src/alert/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use './mixin' as *;
.#{$prefix}-alert {
diff --git a/packages/react/src/anchor/style/index.scss b/packages/react/src/anchor/style/index.scss
index bd7ef79e..e5355e3b 100644
--- a/packages/react/src/anchor/style/index.scss
+++ b/packages/react/src/anchor/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-anchor {
margin: 0;
diff --git a/packages/react/src/aspect-ratio/style/index.scss b/packages/react/src/aspect-ratio/style/index.scss
index b3c66cc0..a121a2de 100644
--- a/packages/react/src/aspect-ratio/style/index.scss
+++ b/packages/react/src/aspect-ratio/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-aspect-ratio {
box-sizing: border-box;
diff --git a/packages/react/src/auto-complete/style/index.scss b/packages/react/src/auto-complete/style/index.scss
index 764014a0..00614608 100644
--- a/packages/react/src/auto-complete/style/index.scss
+++ b/packages/react/src/auto-complete/style/index.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
.#{$prefix}-auto-complete {
position: relative;
diff --git a/packages/react/src/avatar/style/index.scss b/packages/react/src/avatar/style/index.scss
index 3a0e63e5..d581e4ec 100755
--- a/packages/react/src/avatar/style/index.scss
+++ b/packages/react/src/avatar/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-avatar {
box-sizing: border-box;
diff --git a/packages/react/src/back-top/style/index.scss b/packages/react/src/back-top/style/index.scss
index baa7beec..74f2226c 100755
--- a/packages/react/src/back-top/style/index.scss
+++ b/packages/react/src/back-top/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-back-top {
position: fixed;
diff --git a/packages/react/src/badge/style/_mixin.scss b/packages/react/src/badge/style/_mixin.scss
index 46674ccc..d98cf8fe 100644
--- a/packages/react/src/badge/style/_mixin.scss
+++ b/packages/react/src/badge/style/_mixin.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@mixin badge-base {
box-sizing: border-box;
diff --git a/packages/react/src/badge/style/index.scss b/packages/react/src/badge/style/index.scss
index f24ace14..13588970 100755
--- a/packages/react/src/badge/style/index.scss
+++ b/packages/react/src/badge/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/animation' as *;
+@use "../../style/variables" as *;
+@use "../../style/animation" as *;
@use './mixin' as *;
.#{$prefix}-badge {
diff --git a/packages/react/src/breadcrumb/style/index.scss b/packages/react/src/breadcrumb/style/index.scss
index 82000651..0864b225 100755
--- a/packages/react/src/breadcrumb/style/index.scss
+++ b/packages/react/src/breadcrumb/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-breadcrumb {
&__ol {
diff --git a/packages/react/src/button/style/index.scss b/packages/react/src/button/style/index.scss
index 2ef433e7..14e69159 100755
--- a/packages/react/src/button/style/index.scss
+++ b/packages/react/src/button/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/mixins' as *;
+@use "../../style/variables" as *;
+@use "../../style/mixins" as *;
@use './mixin' as *;
$btn-prefix: #{$prefix}-btn;
diff --git a/packages/react/src/calendar/style/index.scss b/packages/react/src/calendar/style/index.scss
index 515367b7..97f88e4a 100644
--- a/packages/react/src/calendar/style/index.scss
+++ b/packages/react/src/calendar/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-calendar {
background: var(--ty-calendar-bg);
diff --git a/packages/react/src/card/style/index.scss b/packages/react/src/card/style/index.scss
index 7e31a54e..7e7131ee 100644
--- a/packages/react/src/card/style/index.scss
+++ b/packages/react/src/card/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use './mixin' as *;
.#{$prefix}-card {
diff --git a/packages/react/src/carousel/style/index.scss b/packages/react/src/carousel/style/index.scss
index def27242..0d3a4ef8 100755
--- a/packages/react/src/carousel/style/index.scss
+++ b/packages/react/src/carousel/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$carousel-prefix: #{$prefix}-carousel;
diff --git a/packages/react/src/cascader/style/index.scss b/packages/react/src/cascader/style/index.scss
index c45a5aaf..589ea7f1 100644
--- a/packages/react/src/cascader/style/index.scss
+++ b/packages/react/src/cascader/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-cascader {
position: relative;
diff --git a/packages/react/src/checkbox/style/index.scss b/packages/react/src/checkbox/style/index.scss
index e986444d..605cb96c 100644
--- a/packages/react/src/checkbox/style/index.scss
+++ b/packages/react/src/checkbox/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-checkbox {
position: relative;
diff --git a/packages/react/src/collapse/style/index.scss b/packages/react/src/collapse/style/index.scss
index 7ccaf432..9a00e2ec 100644
--- a/packages/react/src/collapse/style/index.scss
+++ b/packages/react/src/collapse/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-collapse {
box-sizing: border-box;
diff --git a/packages/react/src/color-picker/style/index.scss b/packages/react/src/color-picker/style/index.scss
index dc6e202a..1da18471 100644
--- a/packages/react/src/color-picker/style/index.scss
+++ b/packages/react/src/color-picker/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-color-picker {
display: inline-block;
diff --git a/packages/react/src/date-picker/style/index.scss b/packages/react/src/date-picker/style/index.scss
index 95482936..6b7c1c42 100755
--- a/packages/react/src/date-picker/style/index.scss
+++ b/packages/react/src/date-picker/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$dp: #{$prefix}-date-picker;
diff --git a/packages/react/src/descriptions/style/index.scss b/packages/react/src/descriptions/style/index.scss
index ec2988c1..75f8ff90 100644
--- a/packages/react/src/descriptions/style/index.scss
+++ b/packages/react/src/descriptions/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-descriptions {
&__body {
diff --git a/packages/react/src/divider/style/index.scss b/packages/react/src/divider/style/index.scss
index 245daac0..69175c16 100755
--- a/packages/react/src/divider/style/index.scss
+++ b/packages/react/src/divider/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-divider {
font-size: var(--ty-divider-font-size);
diff --git a/packages/react/src/drawer/style/index.scss b/packages/react/src/drawer/style/index.scss
index 806ad491..6eea8c65 100644
--- a/packages/react/src/drawer/style/index.scss
+++ b/packages/react/src/drawer/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-drawer {
position: fixed;
diff --git a/packages/react/src/dropdown/style/index.scss b/packages/react/src/dropdown/style/index.scss
index 2147dd0d..9b81a651 100644
--- a/packages/react/src/dropdown/style/index.scss
+++ b/packages/react/src/dropdown/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-dropdown {
diff --git a/packages/react/src/empty/style/index.scss b/packages/react/src/empty/style/index.scss
index 6aa356ea..c5b7b625 100755
--- a/packages/react/src/empty/style/index.scss
+++ b/packages/react/src/empty/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-empty {
display: flex;
diff --git a/packages/react/src/flex/style/index.scss b/packages/react/src/flex/style/index.scss
index cf5f0ddd..4a67aaec 100644
--- a/packages/react/src/flex/style/index.scss
+++ b/packages/react/src/flex/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-flex {
display: flex;
diff --git a/packages/react/src/flip/style/index.scss b/packages/react/src/flip/style/index.scss
index 0849f8ea..cb5b0101 100755
--- a/packages/react/src/flip/style/index.scss
+++ b/packages/react/src/flip/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-flip {
display: inline-block;
diff --git a/packages/react/src/form/style/index.scss b/packages/react/src/form/style/index.scss
index 759dd347..7a950216 100755
--- a/packages/react/src/form/style/index.scss
+++ b/packages/react/src/form/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-form {
&_vertical {
diff --git a/packages/react/src/grid/style/index.scss b/packages/react/src/grid/style/index.scss
index b748b86e..4de647cf 100644
--- a/packages/react/src/grid/style/index.scss
+++ b/packages/react/src/grid/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-grid {
display: grid;
diff --git a/packages/react/src/image/style/index.scss b/packages/react/src/image/style/index.scss
index 628821c0..92b8044e 100644
--- a/packages/react/src/image/style/index.scss
+++ b/packages/react/src/image/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-image {
box-sizing: border-box;
diff --git a/packages/react/src/input-number/style/index.scss b/packages/react/src/input-number/style/index.scss
index 01abb7d3..7fda8f44 100755
--- a/packages/react/src/input-number/style/index.scss
+++ b/packages/react/src/input-number/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use '../../input/style/mixin' as *;
.#{$prefix}-input-number {
diff --git a/packages/react/src/input-otp/style/index.scss b/packages/react/src/input-otp/style/index.scss
index 38a9d9fc..53f447ad 100644
--- a/packages/react/src/input-otp/style/index.scss
+++ b/packages/react/src/input-otp/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use '../../input/style/mixin' as *;
.#{$prefix}-input-otp {
diff --git a/packages/react/src/input-password/style/index.scss b/packages/react/src/input-password/style/index.scss
index 690a208a..361e7859 100755
--- a/packages/react/src/input-password/style/index.scss
+++ b/packages/react/src/input-password/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-input-pwd {
&__suffix {
diff --git a/packages/react/src/input/style/_mixin.scss b/packages/react/src/input/style/_mixin.scss
index 81f521f8..c4295d4e 100755
--- a/packages/react/src/input/style/_mixin.scss
+++ b/packages/react/src/input/style/_mixin.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
// use for the `input` tag
@mixin input-default {
diff --git a/packages/react/src/input/style/index.scss b/packages/react/src/input/style/index.scss
index d644a8ce..274a44a3 100755
--- a/packages/react/src/input/style/index.scss
+++ b/packages/react/src/input/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use './mixin' as *;
.#{$prefix}-input {
diff --git a/packages/react/src/keyboard/style/index.scss b/packages/react/src/keyboard/style/index.scss
index 6feed56e..8599411b 100644
--- a/packages/react/src/keyboard/style/index.scss
+++ b/packages/react/src/keyboard/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-kbd {
font-family: var(--ty-keyboard-font-family);
diff --git a/packages/react/src/layout/style/index.scss b/packages/react/src/layout/style/index.scss
index b2786b3a..4efe9854 100755
--- a/packages/react/src/layout/style/index.scss
+++ b/packages/react/src/layout/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-layout {
box-sizing: border-box;
diff --git a/packages/react/src/link/style/index.scss b/packages/react/src/link/style/index.scss
index 6f92774c..b6ffb91e 100644
--- a/packages/react/src/link/style/index.scss
+++ b/packages/react/src/link/style/index.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
.#{$prefix}-link {
color: var(--ty-color-primary);
diff --git a/packages/react/src/list/style/index.scss b/packages/react/src/list/style/index.scss
index 7c8239b6..59be38c4 100644
--- a/packages/react/src/list/style/index.scss
+++ b/packages/react/src/list/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-list {
color: var(--ty-list-color);
diff --git a/packages/react/src/loader/style/index.scss b/packages/react/src/loader/style/index.scss
index 7f849ddb..5c586ed1 100755
--- a/packages/react/src/loader/style/index.scss
+++ b/packages/react/src/loader/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/mixins' as *;
+@use "../../style/variables" as *;
+@use "../../style/mixins" as *;
.#{$prefix}-loader {
position: relative;
diff --git a/packages/react/src/loading-bar/style/index.scss b/packages/react/src/loading-bar/style/index.scss
index 8c79a386..835eb97e 100755
--- a/packages/react/src/loading-bar/style/index.scss
+++ b/packages/react/src/loading-bar/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-loading-bar {
position: fixed;
diff --git a/packages/react/src/marquee/style/index.scss b/packages/react/src/marquee/style/index.scss
index 24f2904d..7029f3e9 100644
--- a/packages/react/src/marquee/style/index.scss
+++ b/packages/react/src/marquee/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-marquee {
overflow: hidden;
diff --git a/packages/react/src/menu/style/index.scss b/packages/react/src/menu/style/index.scss
index 4192b0d1..11a7b04e 100644
--- a/packages/react/src/menu/style/index.scss
+++ b/packages/react/src/menu/style/index.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
@use "./mixin" as *;
.#{$prefix}-menu {
diff --git a/packages/react/src/message/style/index.scss b/packages/react/src/message/style/index.scss
index 8e9211c2..caedf758 100755
--- a/packages/react/src/message/style/index.scss
+++ b/packages/react/src/message/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/animation' as *;
+@use "../../style/variables" as *;
+@use "../../style/animation" as *;
.#{$prefix}-message {
position: relative;
diff --git a/packages/react/src/modal/style/index.scss b/packages/react/src/modal/style/index.scss
index 2671a67e..4cbfaf7a 100644
--- a/packages/react/src/modal/style/index.scss
+++ b/packages/react/src/modal/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-modal {
position: relative;
diff --git a/packages/react/src/native-select/style/index.scss b/packages/react/src/native-select/style/index.scss
index 5f0010f2..38b8e12d 100755
--- a/packages/react/src/native-select/style/index.scss
+++ b/packages/react/src/native-select/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use './mixin' as *;
$select-arrow: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDEyIDE2Ij4gIDxwYXRoIGZpbGw9IiMzOTNFNDgiIGQ9Ik0wLjIyMiw1LjYxMyBMNS40NTEsMC4yMjMgQzUuNzU2MjEyMDksLTAuMDc0MzYzMjMzMyA2LjI0Mjc4NzkxLC0wLjA3NDM2MzIzMzMgNi41NDgsMC4yMjMgTDExLjc3OCw1LjYxMyBDMTIuMTgsNi4wMjcgMTIuMDI1LDYuNjY3IDExLjQwNyw2LjY2NyBMMC41OTIsNi42NjcgQy0wLjAyNCw2LjY2NyAtMC4xOCw2LjAyNyAwLjIyMiw1LjYxMyBaIE0wLjU5Miw5LjMzMyBMMTEuNDA3LDkuMzMzIEMxMi4wMjUsOS4zMzMgMTIuMTgsOS45NzMgMTEuNzc3LDEwLjM4NyBMNi41NDcsMTUuNzc3IEM2LjI0MTkxMjU4LDE2LjA3MzcxNDMgNS43NTYwODc0MiwxNi4wNzM3MTQzIDUuNDUxLDE1Ljc3NyBMMC4yMjIsMTAuMzg3IEMtMC4xOCw5Ljk3MyAtMC4wMjQsOS4zMzMgMC41OTIsOS4zMzMgWiIvPjwvc3ZnPg==';
diff --git a/packages/react/src/notification/style/index.scss b/packages/react/src/notification/style/index.scss
index dbf8f469..e3f196ca 100755
--- a/packages/react/src/notification/style/index.scss
+++ b/packages/react/src/notification/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-notification {
position: relative;
diff --git a/packages/react/src/overlay/style/index.scss b/packages/react/src/overlay/style/index.scss
index 7b5c57f1..0fc675cf 100755
--- a/packages/react/src/overlay/style/index.scss
+++ b/packages/react/src/overlay/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-overlay {
position: fixed;
diff --git a/packages/react/src/pagination/style/index.scss b/packages/react/src/pagination/style/index.scss
index 3c8ff0a7..e9e6578a 100644
--- a/packages/react/src/pagination/style/index.scss
+++ b/packages/react/src/pagination/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-pagination {
box-sizing: border-box;
diff --git a/packages/react/src/pop-confirm/style/index.scss b/packages/react/src/pop-confirm/style/index.scss
index ad06b11d..cd746293 100755
--- a/packages/react/src/pop-confirm/style/index.scss
+++ b/packages/react/src/pop-confirm/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-pop-confirm {
color: var(--ty-pop-confirm-color);
diff --git a/packages/react/src/popover/style/index.scss b/packages/react/src/popover/style/index.scss
index 0455c883..e4b6b47b 100755
--- a/packages/react/src/popover/style/index.scss
+++ b/packages/react/src/popover/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-popover {
&__title {
diff --git a/packages/react/src/popup/__tests__/popup.test.tsx b/packages/react/src/popup/__tests__/popup.test.tsx
index 6c0f9b97..18d24ce4 100644
--- a/packages/react/src/popup/__tests__/popup.test.tsx
+++ b/packages/react/src/popup/__tests__/popup.test.tsx
@@ -133,4 +133,27 @@ describe(' ', () => {
expect(closingPopup.style.left).toBe('37px');
expect(closingPopup.style.top).toBe('575px');
});
+
+ it('should update popper position when visible content changes', () => {
+ const totalForceUpdateCalls = () =>
+ (createPopper as jest.Mock).mock.results.reduce((total: number, result) => {
+ return total + (result.value?.forceUpdate?.mock.calls.length ?? 0);
+ }, 0);
+
+ const { rerender } = render(
+ Long popup content }>
+ Trigger
+
+ );
+
+ const initialUpdateCount = totalForceUpdateCalls();
+
+ rerender(
+ Short }>
+
Trigger
+
+ );
+
+ expect(totalForceUpdateCalls()).toBe(initialUpdateCount + 1);
+ });
});
diff --git a/packages/react/src/popup/popup.tsx b/packages/react/src/popup/popup.tsx
index 557e6e52..882d5383 100644
--- a/packages/react/src/popup/popup.tsx
+++ b/packages/react/src/popup/popup.tsx
@@ -479,6 +479,14 @@ const Popup = (props: PopupProps): JSX.Element => {
initPopper(popupRef.current);
}, [popupVisible, destroyPopper, initPopper]);
+ useLayoutEffect(() => {
+ if (!popupVisible) {
+ return;
+ }
+
+ popperRef.current?.forceUpdate();
+ });
+
useEffect(() => {
return () => {
clearTimers();
diff --git a/packages/react/src/popup/style/index.scss b/packages/react/src/popup/style/index.scss
index 6ca2d557..0b6f0f29 100755
--- a/packages/react/src/popup/style/index.scss
+++ b/packages/react/src/popup/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-popup {
box-sizing: border-box;
diff --git a/packages/react/src/progress/style/index.scss b/packages/react/src/progress/style/index.scss
index 66e029ee..893ff312 100755
--- a/packages/react/src/progress/style/index.scss
+++ b/packages/react/src/progress/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-progress-bar {
display: flex;
diff --git a/packages/react/src/radio/style/index.scss b/packages/react/src/radio/style/index.scss
index 38a7f57d..939a2d58 100644
--- a/packages/react/src/radio/style/index.scss
+++ b/packages/react/src/radio/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-radio {
position: relative;
diff --git a/packages/react/src/rate/style/index.scss b/packages/react/src/rate/style/index.scss
index 89027a9d..b164f4de 100755
--- a/packages/react/src/rate/style/index.scss
+++ b/packages/react/src/rate/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-rate {
display: inline-flex;
diff --git a/packages/react/src/result/style/index.scss b/packages/react/src/result/style/index.scss
index 64e04420..e48cc2e9 100644
--- a/packages/react/src/result/style/index.scss
+++ b/packages/react/src/result/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/animation' as *;
+@use "../../style/variables" as *;
+@use "../../style/animation" as *;
.#{$prefix}-result {
padding: var(--ty-result-padding);
diff --git a/packages/react/src/row/style/index.scss b/packages/react/src/row/style/index.scss
index 8fd9ff49..0a906e30 100644
--- a/packages/react/src/row/style/index.scss
+++ b/packages/react/src/row/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-row {
box-sizing: border-box;
diff --git a/packages/react/src/scroll-indicator/style/index.scss b/packages/react/src/scroll-indicator/style/index.scss
index fda22de4..f721523d 100644
--- a/packages/react/src/scroll-indicator/style/index.scss
+++ b/packages/react/src/scroll-indicator/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-scroll-indicator {
box-sizing: border-box;
diff --git a/packages/react/src/scroll-number/style/index.scss b/packages/react/src/scroll-number/style/index.scss
index 7516aa69..7f53f122 100644
--- a/packages/react/src/scroll-number/style/index.scss
+++ b/packages/react/src/scroll-number/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-scroll-number {
display: inline-block;
diff --git a/packages/react/src/segmented/style/index.scss b/packages/react/src/segmented/style/index.scss
index d6b8fa97..7d03888c 100644
--- a/packages/react/src/segmented/style/index.scss
+++ b/packages/react/src/segmented/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-segmented {
display: inline-flex;
diff --git a/packages/react/src/select/style/index.scss b/packages/react/src/select/style/index.scss
index 48ec7b75..4bd491a0 100644
--- a/packages/react/src/select/style/index.scss
+++ b/packages/react/src/select/style/index.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
.#{$prefix}-select {
position: relative;
diff --git a/packages/react/src/skeleton/style/index.scss b/packages/react/src/skeleton/style/index.scss
index 20b10cf0..9d9eb220 100644
--- a/packages/react/src/skeleton/style/index.scss
+++ b/packages/react/src/skeleton/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-skeleton {
display: inline-block;
diff --git a/packages/react/src/slider/style/index.scss b/packages/react/src/slider/style/index.scss
index fbd01951..96d03d3e 100644
--- a/packages/react/src/slider/style/index.scss
+++ b/packages/react/src/slider/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-slider {
position: relative;
diff --git a/packages/react/src/space/style/index.scss b/packages/react/src/space/style/index.scss
index db16676a..265d6564 100644
--- a/packages/react/src/space/style/index.scss
+++ b/packages/react/src/space/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-space {
display: inline-flex;
diff --git a/packages/react/src/speed-dial/style/index.scss b/packages/react/src/speed-dial/style/index.scss
index 90476728..a36faf5c 100644
--- a/packages/react/src/speed-dial/style/index.scss
+++ b/packages/react/src/speed-dial/style/index.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
.#{$prefix}-speed-dial {
position: relative;
diff --git a/packages/react/src/split-button/style/index.scss b/packages/react/src/split-button/style/index.scss
index 185a3a34..4b20a323 100644
--- a/packages/react/src/split-button/style/index.scss
+++ b/packages/react/src/split-button/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-split-button {
display: inline-flex;
diff --git a/packages/react/src/split/style/index.scss b/packages/react/src/split/style/index.scss
index 80efe5d4..3144595f 100755
--- a/packages/react/src/split/style/index.scss
+++ b/packages/react/src/split/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-split {
box-sizing: border-box;
diff --git a/packages/react/src/statistic/style/index.scss b/packages/react/src/statistic/style/index.scss
index 02c06965..5fbde228 100644
--- a/packages/react/src/statistic/style/index.scss
+++ b/packages/react/src/statistic/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-statistic {
&__title {
diff --git a/packages/react/src/steps/style/index.scss b/packages/react/src/steps/style/index.scss
index 62c9c755..e965426a 100644
--- a/packages/react/src/steps/style/index.scss
+++ b/packages/react/src/steps/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-steps {
box-sizing: border-box;
diff --git a/packages/react/src/sticky/style/index.scss b/packages/react/src/sticky/style/index.scss
index 4b2dfa76..b6bc81a6 100644
--- a/packages/react/src/sticky/style/index.scss
+++ b/packages/react/src/sticky/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-sticky {
&_fixed {
diff --git a/packages/react/src/strength-indicator/style/index.scss b/packages/react/src/strength-indicator/style/index.scss
index 67009720..fe4391a2 100644
--- a/packages/react/src/strength-indicator/style/index.scss
+++ b/packages/react/src/strength-indicator/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-strength-indicator {
box-sizing: border-box;
diff --git a/packages/tokens/scss/_animation.scss b/packages/react/src/style/_animation.scss
similarity index 100%
rename from packages/tokens/scss/_animation.scss
rename to packages/react/src/style/_animation.scss
diff --git a/packages/tokens/scss/_constants.scss b/packages/react/src/style/_constants.scss
similarity index 100%
rename from packages/tokens/scss/_constants.scss
rename to packages/react/src/style/_constants.scss
diff --git a/packages/tokens/scss/_mixins.scss b/packages/react/src/style/_mixins.scss
similarity index 100%
rename from packages/tokens/scss/_mixins.scss
rename to packages/react/src/style/_mixins.scss
diff --git a/packages/tokens/scss/_normalise.scss b/packages/react/src/style/_normalise.scss
similarity index 100%
rename from packages/tokens/scss/_normalise.scss
rename to packages/react/src/style/_normalise.scss
diff --git a/packages/tokens/scss/_variables.scss b/packages/react/src/style/_variables.scss
similarity index 100%
rename from packages/tokens/scss/_variables.scss
rename to packages/react/src/style/_variables.scss
diff --git a/packages/react/src/style/base.scss b/packages/react/src/style/base.scss
index 80927caa..0d1ba3bc 100644
--- a/packages/react/src/style/base.scss
+++ b/packages/react/src/style/base.scss
@@ -1 +1,3 @@
-@use '@tiny-design/tokens/scss/base' as *;
+@use '@tiny-design/tokens/dist/css/base.css';
+@use './normalise' as *;
+@use './animation' as *;
diff --git a/packages/react/src/style/index.scss b/packages/react/src/style/index.scss
index 44cff52d..da6b1369 100755
--- a/packages/react/src/style/index.scss
+++ b/packages/react/src/style/index.scss
@@ -1,2 +1,4 @@
-@use '@tiny-design/tokens/scss/base' as *;
+@use '@tiny-design/tokens/dist/css/base.css';
+@use './normalise' as *;
+@use './animation' as *;
@use './component' as *;
diff --git a/packages/react/src/switch/style/_mixin.scss b/packages/react/src/switch/style/_mixin.scss
index a77ae869..e963115a 100755
--- a/packages/react/src/switch/style/_mixin.scss
+++ b/packages/react/src/switch/style/_mixin.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/mixins" as *;
+@use "../../style/mixins" as *;
@mixin switch-bg($size) {
width: $size;
diff --git a/packages/react/src/switch/style/index.scss b/packages/react/src/switch/style/index.scss
index 2611a1b8..323e0e04 100644
--- a/packages/react/src/switch/style/index.scss
+++ b/packages/react/src/switch/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use './mixin' as *;
.#{$prefix}-switch {
diff --git a/packages/react/src/table/style/index.scss b/packages/react/src/table/style/index.scss
index 110f916b..879b99ac 100644
--- a/packages/react/src/table/style/index.scss
+++ b/packages/react/src/table/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-table {
color: var(--ty-table-color, var(--ty-color-text));
diff --git a/packages/react/src/tabs/style/index.scss b/packages/react/src/tabs/style/index.scss
index 81587687..b89ec5a1 100644
--- a/packages/react/src/tabs/style/index.scss
+++ b/packages/react/src/tabs/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$tab-prefix: #{$prefix}-tabs;
diff --git a/packages/react/src/tag/style/index.scss b/packages/react/src/tag/style/index.scss
index 75f4a913..d1e6650e 100644
--- a/packages/react/src/tag/style/index.scss
+++ b/packages/react/src/tag/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$tag-preset-colors: 'magenta', 'red', 'volcano', 'orange', 'gold', 'lime', 'green', 'cyan', 'blue',
'geekblue', 'purple';
diff --git a/packages/react/src/text-loop/style/index.scss b/packages/react/src/text-loop/style/index.scss
index 118eb0c4..df4087ab 100644
--- a/packages/react/src/text-loop/style/index.scss
+++ b/packages/react/src/text-loop/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-text-loop {
overflow: hidden;
diff --git a/packages/react/src/textarea/style/index.scss b/packages/react/src/textarea/style/index.scss
index 48509f19..49a815e3 100755
--- a/packages/react/src/textarea/style/index.scss
+++ b/packages/react/src/textarea/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
@use '../../input/style/mixin' as *;
.#{$prefix}-textarea {
diff --git a/packages/react/src/time-picker/style/index.scss b/packages/react/src/time-picker/style/index.scss
index 18665e0c..93c1c4a1 100755
--- a/packages/react/src/time-picker/style/index.scss
+++ b/packages/react/src/time-picker/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$tp: #{$prefix}-time-picker;
diff --git a/packages/react/src/timeline/style/index.scss b/packages/react/src/timeline/style/index.scss
index 9f0c83ff..a97400f0 100755
--- a/packages/react/src/timeline/style/index.scss
+++ b/packages/react/src/timeline/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-timeline {
box-sizing: border-box;
diff --git a/packages/react/src/tooltip/style/index.scss b/packages/react/src/tooltip/style/index.scss
index 5a9eb97f..49e1ed4c 100755
--- a/packages/react/src/tooltip/style/index.scss
+++ b/packages/react/src/tooltip/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-tooltip {
font-size: var(--ty-tooltip-font-size);
diff --git a/packages/react/src/tour/style/index.scss b/packages/react/src/tour/style/index.scss
index 7e6837ac..ff2070b6 100644
--- a/packages/react/src/tour/style/index.scss
+++ b/packages/react/src/tour/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-tour {
position: fixed;
diff --git a/packages/react/src/transfer/style/index.scss b/packages/react/src/transfer/style/index.scss
index 3be9f765..ecc93465 100644
--- a/packages/react/src/transfer/style/index.scss
+++ b/packages/react/src/transfer/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-transfer {
box-sizing: border-box;
diff --git a/packages/react/src/transition/style/_mixin.scss b/packages/react/src/transition/style/_mixin.scss
index a8c09eb4..c904a00a 100644
--- a/packages/react/src/transition/style/_mixin.scss
+++ b/packages/react/src/transition/style/_mixin.scss
@@ -1,4 +1,4 @@
-@use "@tiny-design/tokens/scss/variables" as *;
+@use "../../style/variables" as *;
@mixin zoom-animation(
$direction: 'top',
diff --git a/packages/react/src/tree/style/index.scss b/packages/react/src/tree/style/index.scss
index 8607d840..6d007471 100644
--- a/packages/react/src/tree/style/index.scss
+++ b/packages/react/src/tree/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-tree {
margin: 0;
diff --git a/packages/react/src/typography/style/index.scss b/packages/react/src/typography/style/index.scss
index 2ab333df..56c7da58 100755
--- a/packages/react/src/typography/style/index.scss
+++ b/packages/react/src/typography/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
$tp-prefix: #{$prefix}-typography;
diff --git a/packages/react/src/upload/style/index.scss b/packages/react/src/upload/style/index.scss
index 6ebecc90..d7aa69a7 100644
--- a/packages/react/src/upload/style/index.scss
+++ b/packages/react/src/upload/style/index.scss
@@ -1,5 +1,5 @@
-@use '@tiny-design/tokens/scss/variables' as *;
-@use '@tiny-design/tokens/scss/animation' as *;
+@use "../../style/variables" as *;
+@use "../../style/animation" as *;
.#{$prefix}-upload {
display: inline-block;
diff --git a/packages/react/src/waterfall/style/index.scss b/packages/react/src/waterfall/style/index.scss
index 52978ec8..2a889289 100644
--- a/packages/react/src/waterfall/style/index.scss
+++ b/packages/react/src/waterfall/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-waterfall {
position: relative;
diff --git a/packages/react/src/with-spin/style/index.scss b/packages/react/src/with-spin/style/index.scss
index 2fae210f..5d3c3145 100644
--- a/packages/react/src/with-spin/style/index.scss
+++ b/packages/react/src/with-spin/style/index.scss
@@ -1,4 +1,4 @@
-@use '@tiny-design/tokens/scss/variables' as *;
+@use "../../style/variables" as *;
.#{$prefix}-icon-spin {
animation: ty-rotate 1s linear infinite;
diff --git a/packages/tokens/README.md b/packages/tokens/README.md
index 6dfa2610..5dd1f490 100644
--- a/packages/tokens/README.md
+++ b/packages/tokens/README.md
@@ -1,6 +1,6 @@
# @tiny-design/tokens
-Design tokens, themes, and foundational styles for Tiny Design.
+Design tokens and v2 theme runtime for Tiny Design.
## Install
@@ -14,7 +14,7 @@ pnpm add @tiny-design/tokens
### CSS (recommended)
-Import the compiled CSS to get all tokens, normalization, and animations:
+Import the compiled CSS to get all v2 token CSS variables:
```js
import '@tiny-design/tokens';
@@ -22,26 +22,16 @@ import '@tiny-design/tokens';
import '@tiny-design/tokens/css/base.css';
```
-### SCSS
-
-Import individual SCSS modules for custom builds:
-
-```scss
-@use '@tiny-design/tokens/scss/variables'; // $prefix, breakpoints, structural constants
-@use '@tiny-design/tokens/scss/animation';
-@use '@tiny-design/tokens/scss/mixins';
-```
-
## What's Included
| Module | Description |
| --- | --- |
-| `_variables.scss` | `$prefix`, responsive breakpoints, and `@forward` of structural constants |
-| `_constants.scss` | Compile-time structural constants — padding, sizing, transitions |
-| `_theme.scss` | Theme generation (light/dark via `data-tiny-theme` attribute) |
-| `_normalise.scss` | HTML normalization (based on Normalize.css) |
-| `_animation.scss` | Keyframe animations (`ty-rotate`, `ty-rotate-reverse`, `ty-processing`) |
-| `_mixins.scss` | Helper mixins (e.g. `loader()`) |
+| `css/base.css` | Package CSS entry copied from the base theme CSS |
+| `dist/css/base.css` | v2 light/dark/system theme CSS variables |
+| `dist/css/light.css` | Light theme CSS variables |
+| `dist/css/dark.css` | Dark theme CSS variables |
+| `dist/registry.json` | Token registry for tooling |
+| `runtime/*` | Theme presets, resolver, and validator runtime modules |
## Theming
@@ -62,17 +52,6 @@ Light and dark themes are supported via the `data-tiny-theme` attribute on the d
Three modes are available: `light`, `dark`, and `system` (follows `prefers-color-scheme`).
-## Breakpoints
-
-| Name | Width |
-| --- | --- |
-| `xs` | 480px |
-| `sm` | 600px |
-| `md` | 840px |
-| `lg` | 960px |
-| `xl` | 1280px |
-| `xxl` | 1440px |
-
## License
MIT
diff --git a/packages/tokens/REGISTRY_SPEC.md b/packages/tokens/REGISTRY_SPEC.md
index 23989f91..8fae22c4 100644
--- a/packages/tokens/REGISTRY_SPEC.md
+++ b/packages/tokens/REGISTRY_SPEC.md
@@ -6,7 +6,7 @@ The token registry is the canonical machine-readable index of all supported v2 t
- Theme Studio
- theme validation
- docs generation
-- migration tooling
+- developer tooling
The registry is metadata, not a theme document. It describes what tokens exist, how they map to CSS variables, and what fallback behavior they expect.
@@ -73,7 +73,7 @@ The build step should generate:
- `defaultValue`
Unresolved default value from the source token document.
- `fallback`
- Recommended authored-SCSS fallback target. This field is guidance metadata for component authors and docs tooling; it does not mean the build step will emit an automatic fallback chain in generated CSS.
+ Recommended component style fallback target. This field is guidance metadata for component authors and docs tooling; it does not mean the build step will emit an automatic fallback chain in generated CSS.
- `status`
One of: `active`, `deprecated`, `internal`
## Naming Rules
@@ -83,9 +83,9 @@ The build step should generate:
- New entries must use the primary v2 names directly. Short prefixes like `btn`, `picker`, or `kbd` are not allowed.
## Fallback Rules
-- Primitive tokens should not appear in authored component SCSS.
+- Primitive tokens should not appear in authored component source styles.
- Semantic tokens usually have no registry fallback.
-- Component tokens should include the semantic fallback they are expected to use in authored SCSS.
+- Component tokens should include the semantic fallback they are expected to use in authored component source styles.
Examples:
- `button.bg.primary` -> fallback `--ty-color-primary`
@@ -96,7 +96,7 @@ Examples:
- `active`
Visible in Theme Studio and allowed in theme documents.
- `deprecated`
- Still resolved, but hidden by default in editing UIs and marked for migration.
+ Still resolved, but hidden by default in editing UIs and marked as not preferred for new themes.
- `internal`
Not allowed in user-authored themes.
diff --git a/packages/tokens/package.json b/packages/tokens/package.json
index e632e023..40e6232c 100644
--- a/packages/tokens/package.json
+++ b/packages/tokens/package.json
@@ -1,7 +1,7 @@
{
"name": "@tiny-design/tokens",
"version": "1.11.1",
- "description": "Design tokens, themes, and foundational styles for Tiny UI",
+ "description": "Design tokens and v2 theme runtime for Tiny UI",
"license": "MIT",
"repository": {
"type": "git",
@@ -15,7 +15,6 @@
"exports": {
".": "./css/base.css",
"./css/*": "./css/*",
- "./scss/*": "./scss/*",
"./registry": "./dist/registry.json",
"./presets": {
"types": "./runtime/presets.d.ts",
@@ -40,19 +39,15 @@
"files": [
"css",
"dist",
- "runtime",
- "scss"
+ "runtime"
],
"scripts": {
"build": "node scripts/build.js",
"build:base-css": "node -e \"require('./scripts/build').buildBaseCss()\"",
- "build:runtime": "node build/build-v2.js",
+ "build:runtime": "node scripts/build-runtime.js",
"clean": "rimraf css dist"
},
"devDependencies": {
- "autoprefixer": "^10.4.4",
- "postcss": "^8.4.0",
- "rimraf": "^3.0.2",
- "sass": "^1.49.9"
+ "rimraf": "^3.0.2"
}
}
diff --git a/packages/tokens/build/build-v2.js b/packages/tokens/scripts/build-runtime.js
similarity index 93%
rename from packages/tokens/build/build-v2.js
rename to packages/tokens/scripts/build-runtime.js
index 72447363..ab364707 100644
--- a/packages/tokens/build/build-v2.js
+++ b/packages/tokens/scripts/build-runtime.js
@@ -7,10 +7,8 @@ const DIST_DIR = path.join(ROOT, 'dist');
const DIST_CSS_DIR = path.join(DIST_DIR, 'css');
const REGISTRY_DTS_PATH = path.join(DIST_DIR, 'registry.d.ts');
const PRESETS_DTS_PATH = path.join(DIST_DIR, 'presets.d.ts');
-const LEGACY_ALIAS_MAP_JSON_PATH = path.join(DIST_DIR, 'alias-map.json');
-const LEGACY_ALIAS_MAP_DTS_PATH = path.join(DIST_DIR, 'alias-map.d.ts');
const SCHEMA_DIST_DIR = path.join(DIST_DIR, 'schema');
-const THEME_SCHEMA_PATH = path.join(SOURCE_DIR, 'schema', 'theme.v1.schema.json');
+const THEME_SCHEMA_PATH = path.join(SOURCE_DIR, 'schema', 'theme.schema.json');
const SEMANTIC_DIR = path.join(SOURCE_DIR, 'semantic');
const COMPONENT_DIR = path.join(SOURCE_DIR, 'components');
@@ -28,12 +26,6 @@ function writeJson(filePath, value) {
fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n');
}
-function removeIfExists(filePath) {
- if (fs.existsSync(filePath)) {
- fs.rmSync(filePath);
- }
-}
-
function listJsonFiles(dir) {
if (!fs.existsSync(dir)) return [];
return fs
@@ -315,24 +307,22 @@ function buildRuntimeTokens() {
mkdirp(DIST_CSS_DIR);
mkdirp(SCHEMA_DIST_DIR);
- removeIfExists(LEGACY_ALIAS_MAP_JSON_PATH);
- removeIfExists(LEGACY_ALIAS_MAP_DTS_PATH);
writeJson(path.join(DIST_DIR, 'registry.json'), registry);
writeJson(path.join(DIST_DIR, 'presets.json'), presets);
- fs.writeFileSync(path.join(DIST_CSS_DIR, 'v2-light.css'), lightCss);
- fs.writeFileSync(path.join(DIST_CSS_DIR, 'v2-dark.css'), darkCss);
+ fs.writeFileSync(path.join(DIST_CSS_DIR, 'light.css'), lightCss);
+ fs.writeFileSync(path.join(DIST_CSS_DIR, 'dark.css'), darkCss);
fs.writeFileSync(path.join(DIST_CSS_DIR, 'base.css'), baseCss);
fs.writeFileSync(REGISTRY_DTS_PATH, buildRegistryDts());
fs.writeFileSync(PRESETS_DTS_PATH, buildPresetsDts(presets));
- fs.copyFileSync(THEME_SCHEMA_PATH, path.join(SCHEMA_DIST_DIR, 'theme.v1.schema.json'));
+ fs.copyFileSync(THEME_SCHEMA_PATH, path.join(SCHEMA_DIST_DIR, 'theme.schema.json'));
console.log(' dist/registry.json');
console.log(' dist/registry.d.ts');
console.log(' dist/presets.json');
console.log(' dist/presets.d.ts');
- console.log(' dist/schema/theme.v1.schema.json');
- console.log(' dist/css/v2-light.css');
- console.log(' dist/css/v2-dark.css');
+ console.log(' dist/schema/theme.schema.json');
+ console.log(' dist/css/light.css');
+ console.log(' dist/css/dark.css');
console.log(' dist/css/base.css');
console.log('\nRuntime tokens done.');
}
diff --git a/packages/tokens/scripts/build.js b/packages/tokens/scripts/build.js
index ad3acc0c..f37cce8c 100644
--- a/packages/tokens/scripts/build.js
+++ b/packages/tokens/scripts/build.js
@@ -1,35 +1,26 @@
const fs = require('fs');
const path = require('path');
-const sass = require('sass');
-const postcss = require('postcss');
-const autoprefixer = require('autoprefixer');
-const { buildRuntimeTokens } = require('../build/build-v2');
+const { buildRuntimeTokens } = require('./build-runtime');
const ROOT = path.resolve(__dirname, '..');
-const SCSS_DIR = path.join(ROOT, 'scss');
const CSS_DIR = path.join(ROOT, 'css');
+const V2_BASE_CSS_PATH = path.join(ROOT, 'dist', 'css', 'base.css');
async function buildBaseCss() {
console.log('Building base CSS...\n');
- // Compile scss/base.scss → css/base.css
- const result = sass.compile(path.join(SCSS_DIR, 'base.scss'), {
- loadPaths: [SCSS_DIR],
- });
-
- const processed = await postcss([autoprefixer]).process(result.css, { from: undefined });
+ buildRuntimeTokens();
fs.mkdirSync(CSS_DIR, { recursive: true });
- fs.writeFileSync(path.join(CSS_DIR, 'base.css'), processed.css);
+ fs.copyFileSync(V2_BASE_CSS_PATH, path.join(CSS_DIR, 'base.css'));
- console.log(' css/base.css');
+ console.log(' css/base.css (copied from dist/css/base.css)');
console.log('\nBase CSS done.');
}
async function build() {
console.log('Building tokens package...\n');
await buildBaseCss();
- buildRuntimeTokens();
console.log('\nTokens package done.');
}
diff --git a/packages/tokens/scripts/sync-theme-source-from-scss.js b/packages/tokens/scripts/sync-theme-source-from-scss.js
deleted file mode 100644
index b4768c3c..00000000
--- a/packages/tokens/scripts/sync-theme-source-from-scss.js
+++ /dev/null
@@ -1,554 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-
-const ROOT = path.resolve(__dirname, '..');
-const SOURCE_DIR = path.join(ROOT, 'source');
-const SEMANTIC_DIR = path.join(SOURCE_DIR, 'semantic');
-const COMPONENT_DIR = path.join(SOURCE_DIR, 'components');
-const THEMES_DIR = path.join(SOURCE_DIR, 'themes');
-const LIGHT_THEME_PATH = path.join(ROOT, 'scss', 'themes', '_light.scss');
-const DARK_THEME_PATH = path.join(ROOT, 'scss', 'themes', '_dark.scss');
-
-const COMPONENT_PREFIX_MAP = {
- alert: 'alert',
- anchor: 'anchor',
- avatar: 'avatar',
- 'back-top': 'back-top',
- badge: 'badge',
- btn: 'button',
- calendar: 'calendar',
- card: 'card',
- carousel: 'carousel',
- cascader: 'cascader',
- checkbox: 'checkbox',
- collapse: 'collapse',
- descriptions: 'descriptions',
- description: 'descriptions',
- divider: 'divider',
- drawer: 'drawer',
- empty: 'empty',
- form: 'form',
- input: 'input',
- 'input-number': 'input-number',
- kbd: 'keyboard',
- keyboard: 'keyboard',
- layout: 'layout',
- list: 'list',
- marquee: 'marquee',
- menu: 'menu',
- message: 'message',
- modal: 'modal',
- 'native-select': 'native-select',
- notification: 'notification',
- pagination: 'pagination',
- picker: 'picker',
- popover: 'popover',
- popup: 'popup',
- progress: 'progress',
- radio: 'radio',
- result: 'result',
- segmented: 'segmented',
- select: 'select',
- skeleton: 'skeleton',
- slider: 'slider',
- 'speed-dial': 'speed-dial',
- split: 'split',
- steps: 'steps',
- 'strength-indicator': 'strength-indicator',
- switch: 'switch',
- table: 'table',
- tabs: 'tabs',
- tag: 'tag',
- textarea: 'textarea',
- timeline: 'timeline',
- tooltip: 'tooltip',
- transfer: 'transfer',
- tree: 'tree',
- typography: 'typography',
- upload: 'upload',
-};
-
-const SEMANTIC_EXACT_KEYS = new Set([
- 'border-radius',
- 'font-family',
- 'font-family-monospace',
- 'font-size-base',
- 'font-size-lg',
- 'font-size-sm',
- 'font-weight',
- 'headings-font-weight',
- 'height-lg',
- 'height-md',
- 'height-sm',
- 'line-height-base',
- 'spacer',
-]);
-const SKIP_COMPONENT_PREFIXES = new Set(['btn', 'input', 'card']);
-
-// When multiple semantic keys share the same light+dark value pair,
-// prefer more general tokens over specific ones for component fallback matching.
-const SEMANTIC_PREFERENCE_ORDER = [
- 'color-bg-container',
- 'color-bg-elevated',
- 'color-bg',
- 'color-bg-layout',
- 'color-bg-disabled',
- 'color-bg-spotlight',
- 'color-text',
- 'color-text-secondary',
- 'color-text-heading',
- 'color-text-label',
- 'color-fill',
- 'color-fill-secondary',
- 'color-fill-tertiary',
- 'color-border',
- 'color-border-secondary',
- 'color-border-light',
- 'color-primary',
- 'color-primary-hover',
- 'color-info',
- 'color-success',
- 'color-warning',
- 'color-danger',
-];
-
-function getSemanticFileName(key) {
- if (/^chart-\d+$/.test(key)) return 'charts.json';
- if (key.startsWith('color-')) return 'colors.json';
- if (key.startsWith('shadow-')) return 'effects.json';
- if (
- key.startsWith('font-') ||
- key.startsWith('line-') ||
- /^h[1-6]-font-size$/.test(key) ||
- key === 'headings-font-weight'
- ) {
- return 'typography.json';
- }
- if (key === 'spacer' || key.startsWith('spacing-')) return 'spacing.json';
- if (key === 'border-radius' || key.startsWith('height-')) return 'size.json';
- return 'misc.json';
-}
-
-function preferredSemanticKey(existingKey, newKey) {
- const existingRank = SEMANTIC_PREFERENCE_ORDER.indexOf(existingKey);
- const newRank = SEMANTIC_PREFERENCE_ORDER.indexOf(newKey);
- // Lower index = higher preference. -1 means not in the list (lowest priority).
- if (existingRank === -1 && newRank === -1) return existingKey;
- if (existingRank === -1) return newKey;
- if (newRank === -1) return existingKey;
- return newRank < existingRank ? newKey : existingKey;
-}
-
-function mkdirp(dir) {
- fs.mkdirSync(dir, { recursive: true });
-}
-
-function readJson(filePath) {
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
-}
-
-function writeJson(filePath, value) {
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
-}
-
-function listJsonFiles(dir) {
- if (!fs.existsSync(dir)) return [];
- return fs
- .readdirSync(dir)
- .filter((name) => name.endsWith('.json'))
- .sort()
- .map((name) => path.join(dir, name));
-}
-
-function stripCommentLines(source) {
- return source
- .split('\n')
- .filter((line) => !line.trim().startsWith('//'))
- .join('\n');
-}
-
-function splitTopLevel(source, separator) {
- const parts = [];
- let current = '';
- let parenDepth = 0;
- let braceDepth = 0;
- let bracketDepth = 0;
- let quote = null;
- let escape = false;
-
- for (let i = 0; i < source.length; i += 1) {
- const char = source[i];
-
- if (quote) {
- current += char;
- if (escape) {
- escape = false;
- continue;
- }
- if (char === '\\') {
- escape = true;
- continue;
- }
- if (char === quote) {
- quote = null;
- }
- continue;
- }
-
- if (char === '"' || char === '\'') {
- quote = char;
- current += char;
- continue;
- }
-
- if (char === '(') parenDepth += 1;
- if (char === ')') parenDepth -= 1;
- if (char === '{') braceDepth += 1;
- if (char === '}') braceDepth -= 1;
- if (char === '[') bracketDepth += 1;
- if (char === ']') bracketDepth -= 1;
-
- if (
- char === separator &&
- parenDepth === 0 &&
- braceDepth === 0 &&
- bracketDepth === 0
- ) {
- const trimmed = current.trim();
- if (trimmed) parts.push(trimmed);
- current = '';
- continue;
- }
-
- current += char;
- }
-
- const trimmed = current.trim();
- if (trimmed) parts.push(trimmed);
- return parts;
-}
-
-function splitEntry(entry) {
- let parenDepth = 0;
- let braceDepth = 0;
- let bracketDepth = 0;
- let quote = null;
- let escape = false;
-
- for (let i = 0; i < entry.length; i += 1) {
- const char = entry[i];
-
- if (quote) {
- if (escape) {
- escape = false;
- continue;
- }
- if (char === '\\') {
- escape = true;
- continue;
- }
- if (char === quote) {
- quote = null;
- }
- continue;
- }
-
- if (char === '"' || char === '\'') {
- quote = char;
- continue;
- }
-
- if (char === '(') parenDepth += 1;
- if (char === ')') parenDepth -= 1;
- if (char === '{') braceDepth += 1;
- if (char === '}') braceDepth -= 1;
- if (char === '[') bracketDepth += 1;
- if (char === ']') bracketDepth -= 1;
-
- if (char === ':' && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0) {
- return [entry.slice(0, i).trim(), entry.slice(i + 1).trim()];
- }
- }
-
- throw new Error(`Failed to parse theme entry: ${entry}`);
-}
-
-function normalizeScssValue(rawValue) {
- return rawValue
- .replace(/#\{\s*([^}]+)\s*\}/g, '$1')
- .replace(/'\s*,\s*'/g, ',')
- .replace(/\s+/g, ' ')
- .replace(/\s+,/g, ',')
- .trim();
-}
-
-function parseThemeMap(filePath) {
- const source = stripCommentLines(fs.readFileSync(filePath, 'utf8'));
- const start = source.indexOf('(');
- const end = source.lastIndexOf(');');
- if (start === -1 || end === -1 || end <= start) {
- throw new Error(`Failed to locate theme map in ${filePath}`);
- }
-
- const body = source.slice(start + 1, end);
- const entries = splitTopLevel(body, ',');
- const tokens = {};
-
- for (const entry of entries) {
- const [key, value] = splitEntry(entry);
- tokens[key] = normalizeScssValue(value);
- }
-
- return tokens;
-}
-
-function titleCase(input) {
- return input
- .split('-')
- .filter(Boolean)
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
- .join(' ');
-}
-
-function cssVarForTokenKey(key) {
- return `--ty-${key.replace(/\./g, '-')}`;
-}
-
-function isSemanticKey(key) {
- if (key.startsWith('color-picker-')) return false;
- if (SEMANTIC_EXACT_KEYS.has(key)) return true;
- if (/^h[1-6]-font-size$/.test(key)) return true;
- if (/^chart-\d+$/.test(key)) return true;
- return (
- key.startsWith('color-') ||
- key.startsWith('shadow-') ||
- key.startsWith('font-') ||
- key.startsWith('line-') ||
- key.startsWith('height-')
- );
-}
-
-function inferTokenType(key, value) {
- const lowerValue = String(value).toLowerCase();
-
- if (key.includes('font-family')) return 'font-family';
- if (key.includes('font-weight') || key === 'font-weight') return 'font-weight';
- if (key.includes('line-height')) return 'line-height';
- if (key.includes('duration')) return 'duration';
- if (key.includes('transition')) return 'transition';
- if (key.includes('shadow')) return 'shadow';
-
- if (
- lowerValue.startsWith('#') ||
- lowerValue.startsWith('rgb') ||
- lowerValue.startsWith('hsl') ||
- lowerValue === 'transparent' ||
- lowerValue === 'currentcolor'
- ) {
- return 'color';
- }
-
- if (/^-?\d+(\.\d+)?$/.test(lowerValue)) {
- return key.includes('opacity') ? 'number' : 'string';
- }
-
- if (
- /-?\d/.test(lowerValue) &&
- /(px|rem|em|%|vh|vw|ch)$/.test(lowerValue)
- ) {
- return 'dimension';
- }
-
- if (lowerValue.startsWith('linear-gradient')) return 'string';
-
- return 'string';
-}
-
-function inferDescription(key, category, componentName) {
- const suffix = category === 'component' ? key.split('.').slice(1).join(' ') : key;
- const label = suffix.replace(/-/g, ' ');
- if (category === 'component') {
- return `${titleCase(componentName)} ${label}.`;
- }
- return `${label.replace(/\b\w/g, (char) => char.toUpperCase())}.`;
-}
-
-function findComponentPrefix(key) {
- const prefixes = Object.keys(COMPONENT_PREFIX_MAP).sort((a, b) => b.length - a.length);
- return prefixes.find((prefix) => key === prefix || key.startsWith(`${prefix}-`));
-}
-
-function loadExistingTokenKeys(dir) {
- const keys = new Set();
- for (const filePath of listJsonFiles(dir)) {
- for (const key of Object.keys(readJson(filePath))) {
- keys.add(key);
- }
- }
- return keys;
-}
-
-function syncThemeSourceFromScss() {
- const lightTheme = parseThemeMap(LIGHT_THEME_PATH);
- const darkTheme = parseThemeMap(DARK_THEME_PATH);
- const semanticKeys = loadExistingTokenKeys(SEMANTIC_DIR);
- const componentKeys = loadExistingTokenKeys(COMPONENT_DIR);
-
- const generatedSemantic = {};
- const generatedComponents = {};
- const darkSemanticOverrides = {};
- const darkComponentOverrides = {};
- const semanticValueIndex = new Map();
-
- for (const [key, lightValue] of Object.entries(lightTheme)) {
- if (!isSemanticKey(key)) continue;
-
- const darkValue = darkTheme[key];
- const indexKey = `${lightValue}:::${darkValue}`;
- const existingKey = semanticValueIndex.get(indexKey);
- if (existingKey) {
- semanticValueIndex.set(indexKey, preferredSemanticKey(existingKey, key));
- } else {
- semanticValueIndex.set(indexKey, key);
- }
-
- if (!semanticKeys.has(key)) {
- generatedSemantic[key] = {
- $value: lightValue,
- $type: inferTokenType(key, lightValue),
- description: inferDescription(key, 'semantic'),
- };
- }
-
- if (darkValue !== undefined && darkValue !== lightValue) {
- darkSemanticOverrides[key] = darkValue;
- }
- }
-
- for (const [legacyKey, lightValue] of Object.entries(lightTheme)) {
- if (isSemanticKey(legacyKey)) continue;
-
- const prefix = findComponentPrefix(legacyKey);
- if (!prefix) continue;
- if (SKIP_COMPONENT_PREFIXES.has(prefix)) continue;
-
- const componentName = COMPONENT_PREFIX_MAP[prefix];
- const suffix = legacyKey === prefix ? '' : legacyKey.slice(prefix.length + 1);
- const tokenKey = suffix ? `${componentName}.${suffix}` : componentName;
- const darkValue = darkTheme[legacyKey];
- if (!componentKeys.has(tokenKey)) {
- const semanticMatch = semanticValueIndex.get(`${lightValue}:::${darkValue}`);
- const definition = {
- $value: semanticMatch ? `{${semanticMatch}}` : lightValue,
- $type: inferTokenType(tokenKey, lightValue),
- description: inferDescription(tokenKey, 'component', componentName),
- };
-
- if (semanticMatch) {
- definition.fallback = `--ty-${semanticMatch}`;
- }
-
- if (!generatedComponents[componentName]) {
- generatedComponents[componentName] = {};
- }
- generatedComponents[componentName][tokenKey] = definition;
- }
-
- if (darkValue !== undefined && darkValue !== lightValue) {
- darkComponentOverrides[tokenKey] = darkValue;
- }
- }
-
- mkdirp(SEMANTIC_DIR);
- mkdirp(COMPONENT_DIR);
- mkdirp(THEMES_DIR);
-
- if (Object.keys(generatedSemantic).length > 0) {
- const semanticGroups = new Map();
-
- for (const key of Object.keys(generatedSemantic).sort()) {
- const fileName = getSemanticFileName(key);
- if (!semanticGroups.has(fileName)) {
- semanticGroups.set(fileName, {});
- }
- semanticGroups.get(fileName)[key] = generatedSemantic[key];
- }
-
- for (const [fileName, tokenMap] of semanticGroups.entries()) {
- writeJson(path.join(SEMANTIC_DIR, fileName), tokenMap);
- }
- }
-
- for (const [componentName, tokenMap] of Object.entries(generatedComponents)) {
- const componentOut = {};
- for (const key of Object.keys(tokenMap).sort()) {
- componentOut[key] = tokenMap[key];
- }
- writeJson(path.join(COMPONENT_DIR, `${componentName}.json`), componentOut);
- }
-
- writeJson(path.join(THEMES_DIR, 'light.json'), {
- $schema: 'https://tiny.design/schema/theme.v1.schema.json',
- meta: {
- id: 'tiny-light',
- name: 'Tiny Light',
- schemaVersion: 1,
- },
- mode: 'light',
- tokens: {
- semantic: {},
- components: {},
- },
- });
-
- const persistedComponentKeys = loadExistingTokenKeys(COMPONENT_DIR);
- const resolvedDarkComponentOverrides = {};
-
- for (const [legacyKey, lightValue] of Object.entries(lightTheme)) {
- if (isSemanticKey(legacyKey)) continue;
-
- const prefix = findComponentPrefix(legacyKey);
- if (!prefix) continue;
- if (SKIP_COMPONENT_PREFIXES.has(prefix)) continue;
-
- const componentName = COMPONENT_PREFIX_MAP[prefix];
- const suffix = legacyKey === prefix ? '' : legacyKey.slice(prefix.length + 1);
- const tokenKey = suffix ? `${componentName}.${suffix}` : componentName;
- if (!persistedComponentKeys.has(tokenKey)) continue;
-
- const darkValue = darkTheme[legacyKey];
- if (darkValue !== undefined && darkValue !== lightValue) {
- resolvedDarkComponentOverrides[tokenKey] = darkValue;
- }
- }
-
- const darkThemeOut = {
- $schema: 'https://tiny.design/schema/theme.v1.schema.json',
- meta: {
- id: 'tiny-dark',
- name: 'Tiny Dark',
- schemaVersion: 1,
- },
- mode: 'dark',
- tokens: {
- semantic: {},
- components: {},
- },
- };
-
- for (const key of Object.keys(darkSemanticOverrides).sort()) {
- darkThemeOut.tokens.semantic[key] = darkSemanticOverrides[key];
- }
-
- for (const key of Object.keys(resolvedDarkComponentOverrides).sort()) {
- darkThemeOut.tokens.components[key] = resolvedDarkComponentOverrides[key];
- }
-
- writeJson(path.join(THEMES_DIR, 'dark.json'), darkThemeOut);
-}
-
-module.exports = { parseThemeMap, syncThemeSourceFromScss };
-
-if (require.main === module) {
- syncThemeSourceFromScss();
-}
diff --git a/packages/tokens/scss/_theme.scss b/packages/tokens/scss/_theme.scss
deleted file mode 100644
index aefa4c64..00000000
--- a/packages/tokens/scss/_theme.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-@use './themes/light' as *;
-@use './themes/dark' as *;
-
-// Generate CSS custom properties from a theme map
-@mixin generate-theme-vars($theme) {
- @each $key, $value in $theme {
- --ty-#{$key}: #{$value};
- }
-}
-
-// Light theme (default)
-:root {
- @include generate-theme-vars($light-theme);
-}
-
-// Dark theme (explicit)
-[data-tiny-theme='dark'] {
- @include generate-theme-vars($dark-theme);
-}
-
-// System preference (opt-in via data-tiny-theme="system")
-@media (prefers-color-scheme: dark) {
- [data-tiny-theme='system'] {
- @include generate-theme-vars($dark-theme);
- }
-}
diff --git a/packages/tokens/scss/base.scss b/packages/tokens/scss/base.scss
deleted file mode 100644
index 60b194f4..00000000
--- a/packages/tokens/scss/base.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@use './theme' as *;
-@use './normalise' as *;
-@use './animation' as *;
diff --git a/packages/tokens/scss/themes/_dark.scss b/packages/tokens/scss/themes/_dark.scss
deleted file mode 100644
index 6ee08c9f..00000000
--- a/packages/tokens/scss/themes/_dark.scss
+++ /dev/null
@@ -1,542 +0,0 @@
-// Dark theme token values
-$dark-theme: (
- // ---- Background ----
- color-bg: #141414,
- color-bg-elevated: #1f1f1f,
- color-bg-container: #1f1f1f,
- color-bg-spotlight: #2a2a2a,
- color-bg-disabled: #2a2a2a,
- color-bg-layout: #141414,
-
- // ---- Text ----
- color-text: rgba(255, 255, 255, 0.85),
- color-text-secondary: rgba(255, 255, 255, 0.65),
- color-text-tertiary: rgba(255, 255, 255, 0.45),
- color-text-quaternary: rgba(255, 255, 255, 0.25),
- color-text-heading: rgba(255, 255, 255, 0.85),
- color-text-label: rgba(255, 255, 255, 0.85),
- color-text-placeholder: #5c5c5c,
-
- // ---- Primary ----
- color-primary: #9065d0,
- color-primary-hover: #a882dc,
- color-primary-active: #7a50bf,
- color-primary-bg: #1a1325,
- color-primary-border: #5b3d8f,
- color-primary-bg-hover: #231a33,
- color-primary-text-hover: #a882dc,
-
- // ---- Border ----
- color-border: #424242,
- color-border-secondary: #363636,
- color-border-light: #303030,
- color-border-btn-default: #424242,
-
- // ---- Fill ----
- color-fill: #262626,
- color-fill-secondary: #2a2a2a,
- color-fill-tertiary: #303030,
-
- // ---- Success ----
- color-success: #49aa19,
- color-success-hover: #6abe39,
- color-success-active: #3c8c14,
- color-success-bg: #162312,
- color-success-border: #274916,
- color-success-text: #6abe39,
-
- // ---- Warning ----
- color-warning: #d89614,
- color-warning-hover: #e8b339,
- color-warning-active: #b37a10,
- color-warning-bg: #2b2111,
- color-warning-border: #594214,
- color-warning-text: #e8b339,
-
- // ---- Danger / Error ----
- color-danger: #d32029,
- color-danger-hover: #e84749,
- color-danger-active: #ab1a20,
- color-danger-bg: #2a1215,
- color-danger-border: #58181c,
- color-danger-text: #e84749,
-
- // ---- Info ----
- color-info: #177ddc,
- color-info-hover: #3c9ae8,
- color-info-active: #1268b3,
- color-info-bg: #111d2c,
- color-info-border: #15395b,
- color-info-text: #3c9ae8,
-
- // ---- Shadows ----
- shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.3),
- shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.4),
- shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.5),
- shadow-popup: 0 3px 6px -4px rgba(0, 0, 0, 0.48) #{','} 0 6px 16px 0 rgba(0, 0, 0, 0.32) #{','} 0 9px 28px 8px rgba(0, 0, 0, 0.2),
- shadow-card: 0 1px 6px rgba(0, 0, 0, 0.35),
- shadow-modal: 0 4px 12px rgba(0, 0, 0, 0.45),
- shadow-btn: inset 0 1px 0 rgba(255, 255, 255, 0.05) #{','} 0 1px 1px rgba(0, 0, 0, 0.2),
-
- // ---- Overlay ----
- color-overlay-bg: rgba(0, 0, 0, 0.65),
- color-overlay-inverted: rgba(50, 50, 50, 0.75),
-
- // ---- Component-specific: Button ----
- btn-default-color: rgba(255, 255, 255, 0.85),
- btn-default-bg: #1f1f1f,
- btn-default-border: #424242,
- btn-default-hover-bg: #1f1f1f,
- btn-default-hover-border: #9065d0,
- btn-default-hover-color: #9065d0,
- btn-default-active-bg: #2a2a2a,
- btn-default-active-border: #9065d0,
- btn-default-active-color: #9065d0,
- btn-disabled-color: rgba(255, 255, 255, 0.25),
- btn-disabled-bg: #2a2a2a,
- btn-disabled-border: #424242,
- btn-loading-bg: #1f1f1f,
- btn-ghost-hover-bg: #1a1325,
- btn-ghost-active-bg: #231a33,
- btn-outline-hover-bg: #1a1325,
- btn-outline-active-bg: #231a33,
- btn-link-disabled-color: rgba(255, 255, 255, 0.25),
-
- // ---- Component-specific: Input ----
- input-bg: #1f1f1f,
- input-border: #424242,
- input-disabled-bg: #2a2a2a,
- input-disabled-color: rgba(255, 255, 255, 0.25),
- input-addon-bg: #262626,
- input-focus-shadow: 0 0 0 3px rgba(144, 101, 208, 0.2),
- input-focus-border: rgba(144, 101, 208, 0.8),
-
- // ---- Component-specific: Select ----
- select-dropdown-bg: #1f1f1f,
- select-option-active-bg: #2a2a2a,
- select-option-selected-bg: #1a1325,
- select-option-disabled-bg: #1f1f1f,
-
- // ---- Component-specific: Card ----
- card-bg: #1f1f1f,
- card-border: #363636,
- card-header-color: rgba(255, 255, 255, 0.85),
- card-shadow-border: rgba(0, 0, 0, 0.2),
-
- // ---- Component-specific: Modal ----
- modal-bg: #1f1f1f,
- modal-header-bg: #1f1f1f,
- modal-header-border: #363636,
- modal-footer-border: #363636,
-
- // ---- Component-specific: Drawer ----
- drawer-bg: #1f1f1f,
- drawer-border: #363636,
-
- // ---- Component-specific: Menu ----
- menu-light-bg: #1f1f1f,
- menu-light-color: rgba(255, 255, 255, 0.85),
- menu-light-border: #303030,
- menu-dark-bg: #001529,
- menu-dark-color: rgba(255, 255, 255, 0.65),
- menu-dark-border: #001529,
- menu-divider-color: rgba(255, 255, 255, 0.1),
- menu-group-title-color: rgba(255, 255, 255, 0.45),
-
- // ---- Component-specific: Notification / Message ----
- notification-bg: #1f1f1f,
- notification-close-color: rgba(255, 255, 255, 0.2),
- notification-close-hover: rgba(255, 255, 255, 0.7),
- message-bg: #1f1f1f,
-
- // ---- Component-specific: Badge ----
- badge-shadow: 0 0 0 1.5px #1f1f1f,
-
- // ---- Component-specific: Tag ----
- tag-bg: #262626,
- tag-border: #424242,
- tag-checkable-bg: #1f1f1f,
- tag-magenta-color: #e0529c,
- tag-magenta-bg: #291321,
- tag-magenta-border: #55162b,
- tag-red-color: #e84749,
- tag-red-bg: #2a1215,
- tag-red-border: #58181c,
- tag-volcano-color: #e87040,
- tag-volcano-bg: #2b1611,
- tag-volcano-border: #592716,
- tag-orange-color: #e89a3c,
- tag-orange-bg: #2b1d11,
- tag-orange-border: #593815,
- tag-gold-color: #e8b339,
- tag-gold-bg: #2b2111,
- tag-gold-border: #594214,
- tag-lime-color: #8bbb11,
- tag-lime-bg: #1a2611,
- tag-lime-border: #3e4f13,
- tag-green-color: #6abe39,
- tag-green-bg: #162312,
- tag-green-border: #274916,
- tag-cyan-color: #33bcb7,
- tag-cyan-bg: #112123,
- tag-cyan-border: #144848,
- tag-blue-color: #3c9ae8,
- tag-blue-bg: #111d2c,
- tag-blue-border: #15395b,
- tag-geekblue-color: #5273e0,
- tag-geekblue-bg: #131a2e,
- tag-geekblue-border: #1c2d57,
- tag-purple-color: #854eca,
- tag-purple-bg: #1a1325,
- tag-purple-border: #301c4d,
-
- // ---- Component-specific: Tabs ----
- tabs-border: #303030,
- tabs-card-bg: #262626,
- tabs-card-active-bg: #1f1f1f,
-
- // ---- Component-specific: Collapse ----
- collapse-bg: #262626,
- collapse-border: #424242,
- collapse-content-bg: #1f1f1f,
- collapse-header-hover-bg: #303030,
- collapse-borderless-bg: #1f1f1f,
-
- // ---- Component-specific: Descriptions ----
- descriptions-label-bg: #262626,
- descriptions-border: #363636,
-
- // ---- Component-specific: Steps ----
- steps-tail-color: #424242,
- steps-icon-bg: #1f1f1f,
-
- // ---- Component-specific: Timeline ----
- timeline-line-color: #363636,
- timeline-dot-bg: #1f1f1f,
- timeline-head-bg: #1f1f1f,
-
- // ---- Component-specific: Slider ----
- slider-rail-bg: #363636,
- slider-thumb-bg: #1f1f1f,
- slider-thumb-border: #9065d0,
- slider-dot-bg: #1f1f1f,
- slider-dot-border: #424242,
- slider-dot-active-border: #9065d0,
- slider-mark-color: rgba(255, 255, 255, 0.4),
- slider-mark-active-color: rgba(255, 255, 255, 0.7),
-
- // ---- Component-specific: Progress ----
- progress-trail-bg: #363636,
- progress-text-color: rgba(255, 255, 255, 0.65),
- progress-circle-trail: #363636,
-
- // ---- Component-specific: Skeleton ----
- skeleton-bg: #303030,
- skeleton-shimmer: linear-gradient(to right, #303030 25%, #3a3a3a 37%, #303030 63%),
-
- // ---- Component-specific: Keyboard ----
- kbd-bg: #2a2a2a,
- kbd-border: #424242,
- kbd-border-bottom: #363636,
- kbd-color: rgba(255, 255, 255, 0.85),
- kbd-shadow: inset 0 -1px 0 #363636,
-
- // ---- Component-specific: Transfer ----
- transfer-border: #424242,
- transfer-header-bg: #1f1f1f,
- transfer-item-hover-bg: #2a2a2a,
- transfer-footer-bg: #1f1f1f,
- transfer-footer-border: #303030,
-
- // ---- Component-specific: Upload ----
- upload-dragger-bg: #262626,
- upload-dragger-border: #424242,
- upload-dragger-hover-bg: #303030,
- upload-item-hover-bg: #2a2a2a,
-
- // ---- Component-specific: Date/Time picker ----
- picker-input-bg: #1f1f1f,
- picker-dropdown-bg: #1f1f1f,
- picker-cell-hover-bg: #2a2a2a,
- picker-cell-selected-hover-bg: #7a50bf,
- picker-cell-disabled-bg: #2a2a2a,
- picker-clear-bg: #1f1f1f,
-
- // ---- Component-specific: Color Picker ----
- color-picker-bg: #1f1f1f,
- color-picker-border: #424242,
-
- // ---- Component-specific: List ----
- list-border: #363636,
-
- // ---- Component-specific: Speed Dial ----
- speed-dial-bg: #9065d0,
- speed-dial-color: #fff,
- speed-dial-bg-hover: #7a50bf,
- speed-dial-action-bg: #1f1f1f,
- speed-dial-action-color: rgba(255, 255, 255, 0.85),
- speed-dial-action-bg-hover: #2a2a2a,
- speed-dial-tooltip-bg: #363636,
- speed-dial-tooltip-color: rgba(255, 255, 255, 0.85),
-
- // ---- Component-specific: Split ----
- split-bar-bg: #262626,
- split-bar-border: #424242,
- split-bar-line: #525252,
-
- // ---- Component-specific: Popup ----
- popup-light-bg: #1f1f1f,
- popup-dark-bg: #363636,
- popup-arrow-shadow: rgba(0, 0, 0, 0.2),
-
- // ---- Component-specific: Layout ----
- layout-sidebar-bg: #12131a,
- layout-sidebar-trigger-bg: rgb(0, 33, 64),
- layout-sidebar-light-bg: #1f1f1f,
- layout-sidebar-light-color: rgba(255, 255, 255, 0.85),
- layout-sidebar-light-trigger-bg: #2a2a2a,
- layout-sidebar-light-trigger-icon: #666,
-
- // ---- Component-specific: Anchor ----
- anchor-bg: #1f1f1f,
- anchor-ink-bg: #303030,
- anchor-ball-bg: #1f1f1f,
-
- // ---- Component-specific: Form ----
- form-notice-bg: #2b2111,
- form-notice-color: rgba(255, 255, 255, 0.65),
- form-error-color: #e84749,
- form-error-hover: #d32029,
-
- // ---- Component-specific: Checkbox / Radio ----
- checkbox-bg: #1f1f1f,
- checkbox-border: #424242,
- checkbox-disabled-bg: #2a2a2a,
- checkbox-check-color: #fff,
- radio-bg: #1f1f1f,
- radio-disabled-border: #424242,
- radio-disabled-dot: rgba(255, 255, 255, 0.2),
-
- // ---- Component-specific: Switch ----
- switch-bg: rgba(255, 255, 255, 0.25),
- switch-thumb-bg: #e8e8e8,
- switch-thumb-border: rgba(255, 255, 255, 0.25),
- switch-thumb-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4),
-
- // ---- Component-specific: Divider ----
- divider-color: #363636,
- divider-text-color: rgba(255, 255, 255, 0.85),
-
- // ---- Component-specific: Popover ----
- popover-dark-border: #525252,
-
- // ---- Component-specific: Native select ----
- native-select-bg: #1f1f1f,
- native-select-disabled-bg: #2a2a2a,
- native-select-disabled-color: rgba(255, 255, 255, 0.25),
-
- // ---- Component-specific: Pagination ----
- pagination-bg: #1f1f1f,
- pagination-disabled-bg: #2a2a2a,
- pagination-disabled-active-bg: #424242,
- pagination-disabled-color: #525252,
-
- // ---- Component-specific: Typography ----
- typography-heading-color: rgba(255, 255, 255, 0.85),
- typography-body-color: rgba(255, 255, 255, 0.65),
- typography-code-bg: rgba(255, 255, 255, 0.06),
- typography-code-border: rgba(255, 255, 255, 0.06),
- typography-mark-bg: #594214,
-
- // ---- Component-specific: Result ----
- result-content-bg: #262626,
-
- // ---- Component-specific: Empty ----
- empty-desc-color: rgba(255, 255, 255, 0.35),
-
- // ---- Component-specific: Carousel ----
- carousel-arrow-bg: rgba(255, 255, 255, 0.15),
- carousel-arrow-hover-bg: rgba(255, 255, 255, 0.25),
- carousel-dot-bg: rgba(255, 255, 255, 0.3),
- carousel-dot-hover-bg: rgba(255, 255, 255, 0.6),
- carousel-dot-active-bg: #fff,
-
- // ---- Component-specific: Avatar ----
- avatar-bg: #555,
- avatar-color: #e8e8e8,
- avatar-border: #1f1f1f,
- avatar-presence-shadow: 0 0 0 0.1rem #1f1f1f,
- avatar-offline-color: #525252,
-
- // ---- Component-specific: BackTop ----
- back-top-bg: rgba(255, 255, 255, 0.2),
-
- // ---- Component-specific: Input Number ----
- input-number-control-border: #424242,
- input-number-control-active-bg: #2a2a2a,
- input-number-icon-color: #666,
-
- // ---- Component-specific: Tree ----
- tree-arrow-color: #666,
- tree-hover-bg: #2a2a2a,
-
- // ---- Component-specific: Textarea ----
- textarea-counter-color: rgba(255, 255, 255, 0.45),
-
- // ---- Component-specific: Table ----
- table-header-bg: #262626,
- table-border: #363636,
- table-hover: #2a2a2a,
- table-selected-bg: rgba(144, 101, 208, 0.1),
-
- // ---- Component-specific: Segmented ----
- segmented-bg: #2a2a2a,
- segmented-active-bg: #1f1f1f,
-
- // ---- Component-specific: Cascader ----
- cascader-bg: #1f1f1f,
- cascader-border: #424242,
- cascader-dropdown-bg: #1f1f1f,
- cascader-hover: #2a2a2a,
- cascader-selected-bg: rgba(144, 101, 208, 0.1),
-
- // ---- Component-specific: Calendar ----
- calendar-bg: #1f1f1f,
- calendar-border: #363636,
- calendar-hover: #2a2a2a,
-
- // ---- Typography ----
- // stylelint-disable-next-line scss/operator-no-unspaced
- font-family: #{-apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"},
- font-family-monospace: #{lucida console, consolas, monaco, andale mono, ubuntu mono, monospace},
- font-size-base: 1rem,
- font-size-sm: 0.875rem,
- font-size-lg: 1.25rem,
- font-weight: 400,
- line-height-base: 1.5,
- headings-font-weight: 500,
- h1-font-size: 2.5rem,
- h2-font-size: 2rem,
- h3-font-size: 1.75rem,
- h4-font-size: 1.5rem,
- h5-font-size: 1.25rem,
- h6-font-size: 1rem,
-
- // ---- Sizing ----
- border-radius: 2px,
- height-sm: 24px,
- height-md: 35px,
- height-lg: 44px,
- spacer: 1rem,
-
- // ---- Chart ----
- chart-1: #9065d0,
- chart-2: #177ddc,
- chart-3: #49aa19,
- chart-4: #d89614,
- chart-5: #d32029,
-
- // ---- Component-specific: Button (structural) ----
- btn-min-width: 50px,
- btn-round-radius: 30px,
- btn-group-gap: 10px,
- btn-group-divider-color: rgb(255 255 255 / 20%),
-
- // ---- Component-specific: Input (structural) ----
- input-affix-margin: 0 8px,
- input-clear-size: 14px,
- input-addon-padding: 0 7px,
-
- // ---- Component-specific: Card (structural) ----
- card-header-font-size: 16px,
- card-header-font-weight: 500,
-
- // ---- Component-specific: Select (structural) ----
- select-option-padding: 7px 12px,
- select-option-font-size: 14px,
- select-tag-height: 22px,
- select-suffix-size: 14px,
-
- // ---- Component-specific: Notification (structural) ----
- notification-padding: 16px 24px,
- notification-border-radius: 3px,
- notification-title-font-size: 16px,
-
- // ---- Structural: Alert ----
- alert-border-radius: 3px,
-
- // ---- Structural: Avatar ----
- avatar-border-radius: 2px,
-
- // ---- Structural: Badge ----
- badge-font-size: 12px,
- badge-size: 18px,
- badge-dot-size: 6px,
-
- // ---- Structural: Button ----
- btn-padding-sm: 0 10px,
- btn-padding-md: 0 15px,
- btn-padding-lg: 0 28px,
- btn-loading-opacity: 0.35,
- btn-transition: #{color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out},
-
- // ---- Structural: Card ----
- card-header-padding: 13px 16px,
- card-body-padding: 16px,
- card-footer-padding: 5px 16px 16px,
-
- // ---- Structural: Description ----
- description-sm-padding-vt: 8px,
- description-md-padding-vt: 12px,
- description-lg-padding-vt: 16px,
- description-sm-padding-hr: 16px,
- description-md-padding-hr: 24px,
- description-lg-padding-hr: 24px,
-
- // ---- Structural: Input ----
- input-sm-padding: 0 4px,
- input-md-padding: 0 6px,
- input-lg-padding: 0 8px,
-
- // ---- Structural: Menu ----
- menu-item-padding-vertical: 15px 20px,
-
- // ---- Structural: Native Select ----
- native-select-sm-padding: 3px 25px 3px 7px,
- native-select-md-padding: 6px 25px 6px 7px,
- native-select-lg-padding: 9px 25px 9px 7px,
-
- // ---- Structural: Notification ----
- notification-width: 380px,
- notification-margin: 20px,
-
- // ---- Structural: Popover ----
- popover-arrow-size: 8px,
-
- // ---- Structural: Select ----
- select-selected-font-weight: 600,
- select-dropdown-max-height: 300px,
- select-dropdown-shadow: #{0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%), 0 9px 28px 8px rgb(0 0 0 / 5%)},
-
- // ---- Structural: Slider ----
- slider-size: 12px,
- slider-track-size: 4px,
-
- // ---- Structural: Steps ----
- steps-title-font-size: 16px,
-
- // ---- Structural: Strength Indicator ----
- strength-indicator-border-radius: 99px,
-
- // ---- Structural: Switch ----
- switch-md-font-size: 12px,
- switch-sm-font-size: 9px,
- switch-lg-font-size: 14px,
-
- // ---- Structural: Textarea ----
- textarea-padding: 5px,
-
- // ---- Structural: Tooltip ----
- tooltip-arrow-size: 4px,
- tooltip-content-padding: 5px 8px,
-);
diff --git a/packages/tokens/scss/themes/_light.scss b/packages/tokens/scss/themes/_light.scss
deleted file mode 100644
index d143ffe4..00000000
--- a/packages/tokens/scss/themes/_light.scss
+++ /dev/null
@@ -1,542 +0,0 @@
-// Light theme token values
-$light-theme: (
- // ---- Background ----
- color-bg: #fff,
- color-bg-elevated: #fff,
- color-bg-container: #fff,
- color-bg-spotlight: #f5f5f5,
- color-bg-disabled: #f5f5f5,
- color-bg-layout: #fff,
-
- // ---- Text ----
- color-text: rgba(0, 0, 0, 0.85),
- color-text-secondary: rgba(0, 0, 0, 0.65),
- color-text-tertiary: rgba(0, 0, 0, 0.45),
- color-text-quaternary: rgba(0, 0, 0, 0.25),
- color-text-heading: rgba(0, 0, 0, 0.85),
- color-text-label: rgba(0, 0, 0, 0.85),
- color-text-placeholder: #bfbfbf,
-
- // ---- Primary ----
- color-primary: #6e41bf,
- color-primary-hover: #8b62d0,
- color-primary-active: #5a30a8,
- color-primary-bg: #f3eefa,
- color-primary-border: #c4a7e6,
- color-primary-bg-hover: #ece3f7,
- color-primary-text-hover: #8b62d0,
-
- // ---- Border ----
- color-border: #d9d9d9,
- color-border-secondary: #e8e8e8,
- color-border-light: #f0f0f0,
- color-border-btn-default: #d0d0d5,
-
- // ---- Fill ----
- color-fill: #fafafa,
- color-fill-secondary: #f5f5f5,
- color-fill-tertiary: #f0f0f0,
-
- // ---- Success ----
- color-success: #52c41a,
- color-success-hover: #73d13d,
- color-success-active: #389e0d,
- color-success-bg: #f6ffed,
- color-success-border: #b7eb8f,
- color-success-text: #49b10e,
-
- // ---- Warning ----
- color-warning: #ff9800,
- color-warning-hover: #ffad33,
- color-warning-active: #e68a00,
- color-warning-bg: #fffbe6,
- color-warning-border: #ffe58f,
- color-warning-text: #d48806,
-
- // ---- Danger / Error ----
- color-danger: #f44336,
- color-danger-hover: #ff7875,
- color-danger-active: #cf1322,
- color-danger-bg: #fff1f0,
- color-danger-border: #ffa39e,
- color-danger-text: #cf1322,
-
- // ---- Info ----
- color-info: #1890ff,
- color-info-hover: #40a9ff,
- color-info-active: #096dd9,
- color-info-bg: #e6f7ff,
- color-info-border: #91d5ff,
- color-info-text: #096dd9,
-
- // ---- Shadows ----
- shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075),
- shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15),
- shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175),
- shadow-popup: 0 3px 6px -4px rgba(0, 0, 0, 0.12) #{','} 0 6px 16px 0 rgba(0, 0, 0, 0.08) #{','} 0 9px 28px 8px rgba(0, 0, 0, 0.05),
- shadow-card: 0 1px 6px rgba(0, 0, 0, 0.12),
- shadow-modal: 0 4px 12px rgba(0, 0, 0, 0.15),
- shadow-btn: inset 0 1px 0 rgba(255, 255, 255, 0.15) #{','} 0 1px 1px rgba(0, 0, 0, 0.075),
-
- // ---- Overlay ----
- color-overlay-bg: rgba(0, 0, 0, 0.55),
- color-overlay-inverted: rgba(255, 255, 255, 0.75),
-
- // ---- Component-specific: Button ----
- btn-default-color: #32325d,
- btn-default-bg: #fff,
- btn-default-border: #d0d0d5,
- btn-default-hover-bg: #fff,
- btn-default-hover-border: #6e41bf,
- btn-default-hover-color: #6e41bf,
- btn-default-active-bg: #f2f2f2,
- btn-default-active-border: #6e41bf,
- btn-default-active-color: #6e41bf,
- btn-disabled-color: rgba(0, 0, 0, 0.25),
- btn-disabled-bg: #f5f5f5,
- btn-disabled-border: #d9d9d9,
- btn-loading-bg: #fff,
- btn-ghost-hover-bg: #f3eefa,
- btn-ghost-active-bg: #ece3f7,
- btn-outline-hover-bg: #f3eefa,
- btn-outline-active-bg: #ece3f7,
- btn-link-disabled-color: rgba(0, 0, 0, 0.25),
-
- // ---- Component-specific: Input ----
- input-bg: #fff,
- input-border: #d9d9d9,
- input-disabled-bg: #f4f4f5,
- input-disabled-color: #999,
- input-addon-bg: #fafafa,
- input-focus-shadow: 0 0 0 3px rgba(110, 65, 191, 0.2),
- input-focus-border: rgba(110, 65, 191, 0.8),
-
- // ---- Component-specific: Select ----
- select-dropdown-bg: #fff,
- select-option-active-bg: #f5f5f5,
- select-option-selected-bg: #f3eefa,
- select-option-disabled-bg: #fff,
-
- // ---- Component-specific: Card ----
- card-bg: #fff,
- card-border: #e8e8e8,
- card-header-color: rgba(0, 0, 0, 0.85),
- card-shadow-border: rgba(0, 0, 0, 0.07),
-
- // ---- Component-specific: Modal ----
- modal-bg: #fff,
- modal-header-bg: #fff,
- modal-header-border: #e8e8e8,
- modal-footer-border: #e8e8e8,
-
- // ---- Component-specific: Drawer ----
- drawer-bg: #fff,
- drawer-border: #e8e8e8,
-
- // ---- Component-specific: Menu ----
- menu-light-bg: #fff,
- menu-light-color: #32325d,
- menu-light-border: #f0f0f0,
- menu-dark-bg: #001529,
- menu-dark-color: rgba(255, 255, 255, 0.65),
- menu-dark-border: #001529,
- menu-divider-color: rgba(0, 0, 0, 0.1),
- menu-group-title-color: rgba(0, 0, 0, 0.45),
-
- // ---- Component-specific: Notification / Message ----
- notification-bg: #fff,
- notification-close-color: rgba(0, 0, 0, 0.2),
- notification-close-hover: rgba(0, 0, 0, 0.7),
- message-bg: #fff,
-
- // ---- Component-specific: Badge ----
- badge-shadow: 0 0 0 1.5px #fff,
-
- // ---- Component-specific: Tag ----
- tag-bg: #fafafa,
- tag-border: #d9d9d9,
- tag-checkable-bg: #fff,
- tag-magenta-color: #eb2f96,
- tag-magenta-bg: #fff0f6,
- tag-magenta-border: #ffadd2,
- tag-red-color: #f5222d,
- tag-red-bg: #fff1f0,
- tag-red-border: #ffa39e,
- tag-volcano-color: #fa541c,
- tag-volcano-bg: #fff2e8,
- tag-volcano-border: #ffbb96,
- tag-orange-color: #fa8c16,
- tag-orange-bg: #fff7e6,
- tag-orange-border: #ffd591,
- tag-gold-color: #faad14,
- tag-gold-bg: #fffbe6,
- tag-gold-border: #ffe58f,
- tag-lime-color: #a0d911,
- tag-lime-bg: #fcffe6,
- tag-lime-border: #eaff8f,
- tag-green-color: #52c41a,
- tag-green-bg: #f6ffed,
- tag-green-border: #b7eb8f,
- tag-cyan-color: #13c2c2,
- tag-cyan-bg: #e6fffb,
- tag-cyan-border: #87e8de,
- tag-blue-color: #1890ff,
- tag-blue-bg: #e6f7ff,
- tag-blue-border: #91d5ff,
- tag-geekblue-color: #2f54eb,
- tag-geekblue-bg: #f0f5ff,
- tag-geekblue-border: #adc6ff,
- tag-purple-color: #722ed1,
- tag-purple-bg: #f9f0ff,
- tag-purple-border: #d3adf7,
-
- // ---- Component-specific: Tabs ----
- tabs-border: #f0f0f0,
- tabs-card-bg: #fafafa,
- tabs-card-active-bg: #fff,
-
- // ---- Component-specific: Collapse ----
- collapse-bg: #fafafa,
- collapse-border: #d9d9d9,
- collapse-content-bg: #fff,
- collapse-header-hover-bg: #efefef,
- collapse-borderless-bg: #fff,
-
- // ---- Component-specific: Descriptions ----
- descriptions-label-bg: #fafafa,
- descriptions-border: #dfe2e5,
-
- // ---- Component-specific: Steps ----
- steps-tail-color: #dcdcdc,
- steps-icon-bg: #fff,
-
- // ---- Component-specific: Timeline ----
- timeline-line-color: #e8e8e8,
- timeline-dot-bg: #fff,
- timeline-head-bg: #fff,
-
- // ---- Component-specific: Slider ----
- slider-rail-bg: #e4e8f1,
- slider-thumb-bg: rgb(245, 248, 250),
- slider-thumb-border: #9570d4,
- slider-dot-bg: #fff,
- slider-dot-border: #f0f0f0,
- slider-dot-active-border: #9570d4,
- slider-mark-color: rgba(0, 0, 0, 0.4),
- slider-mark-active-color: rgba(0, 0, 0, 0.7),
-
- // ---- Component-specific: Progress ----
- progress-trail-bg: #e4e8f1,
- progress-text-color: #48576a,
- progress-circle-trail: #e5e9f2,
-
- // ---- Component-specific: Skeleton ----
- skeleton-bg: #f2f2f2,
- skeleton-shimmer: linear-gradient(to right, #f2f2f2 25%, #e6e6e6 37%, #f2f2f2 63%),
-
- // ---- Component-specific: Keyboard ----
- kbd-bg: #f6f6f6,
- kbd-border: #d8d8d8,
- kbd-border-bottom: #ccc,
- kbd-color: #333,
- kbd-shadow: inset 0 -1px 0 #ccc,
-
- // ---- Component-specific: Transfer ----
- transfer-border: #d9d9d9,
- transfer-header-bg: #fff,
- transfer-item-hover-bg: #f5f5f5,
- transfer-footer-bg: #fff,
- transfer-footer-border: #f0f0f0,
-
- // ---- Component-specific: Upload ----
- upload-dragger-bg: #fafafa,
- upload-dragger-border: #d9d9d9,
- upload-dragger-hover-bg: #efefef,
- upload-item-hover-bg: #f5f5f5,
-
- // ---- Component-specific: Date/Time picker ----
- picker-input-bg: #fff,
- picker-dropdown-bg: #fff,
- picker-cell-hover-bg: #f5f5f5,
- picker-cell-selected-hover-bg: #5a30a8,
- picker-cell-disabled-bg: #f5f5f5,
- picker-clear-bg: #fff,
-
- // ---- Component-specific: Color Picker ----
- color-picker-bg: #fff,
- color-picker-border: #dee2e6,
-
- // ---- Component-specific: List ----
- list-border: #dee2e6,
-
- // ---- Component-specific: Speed Dial ----
- speed-dial-bg: #6e41bf,
- speed-dial-color: #fff,
- speed-dial-bg-hover: #5a30a8,
- speed-dial-action-bg: #fff,
- speed-dial-action-color: #32325d,
- speed-dial-action-bg-hover: #f6f9fc,
- speed-dial-tooltip-bg: #32325d,
- speed-dial-tooltip-color: #fff,
-
- // ---- Component-specific: Split ----
- split-bar-bg: #f8f8f9,
- split-bar-border: #dcdee2,
- split-bar-line: #d5d5d5,
-
- // ---- Component-specific: Popup ----
- popup-light-bg: #fff,
- popup-dark-bg: #262626,
- popup-arrow-shadow: rgba(0, 0, 0, 0.07),
-
- // ---- Component-specific: Layout ----
- layout-sidebar-bg: #12131a,
- layout-sidebar-trigger-bg: rgb(0, 33, 64),
- layout-sidebar-light-bg: #fff,
- layout-sidebar-light-color: #333,
- layout-sidebar-light-trigger-bg: #efefef,
- layout-sidebar-light-trigger-icon: #bbb,
-
- // ---- Component-specific: Anchor ----
- anchor-bg: #fff,
- anchor-ink-bg: #f0f0f0,
- anchor-ball-bg: #fff,
-
- // ---- Component-specific: Form ----
- form-notice-bg: #fff7cc,
- form-notice-color: #555,
- form-error-color: #ff4d4f,
- form-error-hover: #ff7875,
-
- // ---- Component-specific: Checkbox / Radio ----
- checkbox-bg: #fff,
- checkbox-border: #d9d9d9,
- checkbox-disabled-bg: #f5f5f5,
- checkbox-check-color: #fff,
- radio-bg: #fff,
- radio-disabled-border: #d9d9d9,
- radio-disabled-dot: rgba(0, 0, 0, 0.2),
-
- // ---- Component-specific: Switch ----
- switch-bg: rgba(0, 0, 0, 0.25),
- switch-thumb-bg: #fff,
- switch-thumb-border: rgba(0, 0, 0, 0.25),
- switch-thumb-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2),
-
- // ---- Component-specific: Divider ----
- divider-color: #e4e4e4,
- divider-text-color: #333,
-
- // ---- Component-specific: Popover ----
- popover-dark-border: #4a4a4a,
-
- // ---- Component-specific: Native select ----
- native-select-bg: #fff,
- native-select-disabled-bg: #ddd,
- native-select-disabled-color: #a5a5a5,
-
- // ---- Component-specific: Pagination ----
- pagination-bg: #fff,
- pagination-disabled-bg: #f5f5f5,
- pagination-disabled-active-bg: #dbdbdb,
- pagination-disabled-color: #d9d9d9,
-
- // ---- Component-specific: Typography ----
- typography-heading-color: rgba(0, 0, 0, 0.85),
- typography-body-color: rgba(0, 0, 0, 0.65),
- typography-code-bg: rgba(0, 0, 0, 0.06),
- typography-code-border: rgba(0, 0, 0, 0.06),
- typography-mark-bg: #ffe58f,
-
- // ---- Component-specific: Result ----
- result-content-bg: #fafafa,
-
- // ---- Component-specific: Empty ----
- empty-desc-color: rgba(0, 0, 0, 0.35),
-
- // ---- Component-specific: Carousel ----
- carousel-arrow-bg: rgba(0, 0, 0, 0.25),
- carousel-arrow-hover-bg: rgba(0, 0, 0, 0.45),
- carousel-dot-bg: rgba(255, 255, 255, 0.3),
- carousel-dot-hover-bg: rgba(255, 255, 255, 0.6),
- carousel-dot-active-bg: #fff,
-
- // ---- Component-specific: Avatar ----
- avatar-bg: #ccc,
- avatar-color: #fff,
- avatar-border: #fff,
- avatar-presence-shadow: 0 0 0 0.1rem #fff,
- avatar-offline-color: #ced4da,
-
- // ---- Component-specific: BackTop ----
- back-top-bg: rgba(0, 0, 0, 0.3),
-
- // ---- Component-specific: Input Number ----
- input-number-control-border: #d9d9d9,
- input-number-control-active-bg: #f4f4f4,
- input-number-icon-color: #999,
-
- // ---- Component-specific: Tree ----
- tree-arrow-color: #999,
- tree-hover-bg: #f5f5f5,
-
- // ---- Component-specific: Textarea ----
- textarea-counter-color: #666,
-
- // ---- Component-specific: Table ----
- table-header-bg: #f6f9fc,
- table-border: #e9ecef,
- table-hover: #f6f9fc,
- table-selected-bg: rgba(110, 65, 191, 0.06),
-
- // ---- Component-specific: Segmented ----
- segmented-bg: #e9ecef,
- segmented-active-bg: #fff,
-
- // ---- Component-specific: Cascader ----
- cascader-bg: #fff,
- cascader-border: #d9d9d9,
- cascader-dropdown-bg: #fff,
- cascader-hover: #f5f5f5,
- cascader-selected-bg: rgba(110, 65, 191, 0.06),
-
- // ---- Component-specific: Calendar ----
- calendar-bg: #fff,
- calendar-border: #e9ecef,
- calendar-hover: #f6f9fc,
-
- // ---- Typography ----
- // stylelint-disable-next-line scss/operator-no-unspaced
- font-family: #{-apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"},
- font-family-monospace: #{lucida console, consolas, monaco, andale mono, ubuntu mono, monospace},
- font-size-base: 1rem,
- font-size-sm: 0.875rem,
- font-size-lg: 1.25rem,
- font-weight: 400,
- line-height-base: 1.5,
- headings-font-weight: 500,
- h1-font-size: 2.5rem,
- h2-font-size: 2rem,
- h3-font-size: 1.75rem,
- h4-font-size: 1.5rem,
- h5-font-size: 1.25rem,
- h6-font-size: 1rem,
-
- // ---- Sizing ----
- border-radius: 2px,
- height-sm: 24px,
- height-md: 35px,
- height-lg: 44px,
- spacer: 1rem,
-
- // ---- Chart ----
- chart-1: #6e41bf,
- chart-2: #1890ff,
- chart-3: #52c41a,
- chart-4: #ff9800,
- chart-5: #f44336,
-
- // ---- Component-specific: Button (structural) ----
- btn-min-width: 50px,
- btn-round-radius: 30px,
- btn-group-gap: 10px,
- btn-group-divider-color: rgb(255 255 255 / 20%),
-
- // ---- Component-specific: Input (structural) ----
- input-affix-margin: 0 8px,
- input-clear-size: 14px,
- input-addon-padding: 0 7px,
-
- // ---- Component-specific: Card (structural) ----
- card-header-font-size: 16px,
- card-header-font-weight: 500,
-
- // ---- Component-specific: Select (structural) ----
- select-option-padding: 7px 12px,
- select-option-font-size: 14px,
- select-tag-height: 22px,
- select-suffix-size: 14px,
-
- // ---- Component-specific: Notification (structural) ----
- notification-padding: 16px 24px,
- notification-border-radius: 3px,
- notification-title-font-size: 16px,
-
- // ---- Structural: Alert ----
- alert-border-radius: 3px,
-
- // ---- Structural: Avatar ----
- avatar-border-radius: 2px,
-
- // ---- Structural: Badge ----
- badge-font-size: 12px,
- badge-size: 18px,
- badge-dot-size: 6px,
-
- // ---- Structural: Button ----
- btn-padding-sm: 0 10px,
- btn-padding-md: 0 15px,
- btn-padding-lg: 0 28px,
- btn-loading-opacity: 0.35,
- btn-transition: #{color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out},
-
- // ---- Structural: Card ----
- card-header-padding: 13px 16px,
- card-body-padding: 16px,
- card-footer-padding: 5px 16px 16px,
-
- // ---- Structural: Description ----
- description-sm-padding-vt: 8px,
- description-md-padding-vt: 12px,
- description-lg-padding-vt: 16px,
- description-sm-padding-hr: 16px,
- description-md-padding-hr: 24px,
- description-lg-padding-hr: 24px,
-
- // ---- Structural: Input ----
- input-sm-padding: 0 4px,
- input-md-padding: 0 6px,
- input-lg-padding: 0 8px,
-
- // ---- Structural: Menu ----
- menu-item-padding-vertical: 15px 20px,
-
- // ---- Structural: Native Select ----
- native-select-sm-padding: 3px 25px 3px 7px,
- native-select-md-padding: 6px 25px 6px 7px,
- native-select-lg-padding: 9px 25px 9px 7px,
-
- // ---- Structural: Notification ----
- notification-width: 380px,
- notification-margin: 20px,
-
- // ---- Structural: Popover ----
- popover-arrow-size: 8px,
-
- // ---- Structural: Select ----
- select-selected-font-weight: 600,
- select-dropdown-max-height: 300px,
- select-dropdown-shadow: #{0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%), 0 9px 28px 8px rgb(0 0 0 / 5%)},
-
- // ---- Structural: Slider ----
- slider-size: 12px,
- slider-track-size: 4px,
-
- // ---- Structural: Steps ----
- steps-title-font-size: 16px,
-
- // ---- Structural: Strength Indicator ----
- strength-indicator-border-radius: 99px,
-
- // ---- Structural: Switch ----
- switch-md-font-size: 12px,
- switch-sm-font-size: 9px,
- switch-lg-font-size: 14px,
-
- // ---- Structural: Textarea ----
- textarea-padding: 5px,
-
- // ---- Structural: Tooltip ----
- tooltip-arrow-size: 4px,
- tooltip-content-padding: 5px 8px,
-);
diff --git a/packages/tokens/source/schema/theme.v1.schema.json b/packages/tokens/source/schema/theme.schema.json
similarity index 94%
rename from packages/tokens/source/schema/theme.v1.schema.json
rename to packages/tokens/source/schema/theme.schema.json
index b3c8c158..b9b9a9be 100644
--- a/packages/tokens/source/schema/theme.v1.schema.json
+++ b/packages/tokens/source/schema/theme.schema.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://tiny.design/schema/theme.v1.schema.json",
- "title": "Tiny Design Theme Document v1",
+ "$id": "https://tiny.design/schema/theme.schema.json",
+ "title": "Tiny Design Theme Document",
"description": "Versioned theme document for Tiny Design token overrides.",
"type": "object",
"additionalProperties": false,
@@ -108,7 +108,7 @@
},
"examples": [
{
- "$schema": "https://tiny.design/schema/theme.v1.schema.json",
+ "$schema": "https://tiny.design/schema/theme.schema.json",
"meta": {
"id": "ocean-glass",
"name": "Ocean Glass",
diff --git a/packages/tokens/source/themes/dark.json b/packages/tokens/source/themes/dark.json
index c7511d20..21001ea6 100644
--- a/packages/tokens/source/themes/dark.json
+++ b/packages/tokens/source/themes/dark.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://tiny.design/schema/theme.v1.schema.json",
+ "$schema": "https://tiny.design/schema/theme.schema.json",
"meta": {
"id": "tiny-dark",
"name": "Tiny Dark",
diff --git a/packages/tokens/source/themes/light.json b/packages/tokens/source/themes/light.json
index b794ba73..b5c7be18 100644
--- a/packages/tokens/source/themes/light.json
+++ b/packages/tokens/source/themes/light.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://tiny.design/schema/theme.v1.schema.json",
+ "$schema": "https://tiny.design/schema/theme.schema.json",
"meta": {
"id": "tiny-light",
"name": "Tiny Light",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1771debd..f8d061d6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
'@eslint/js':
specifier: ^9.0.0
version: 9.39.4
+ '@playwright/test':
+ specifier: ^1.59.1
+ version: 1.59.1
eslint:
specifier: ^9.0.0
version: 9.39.4(jiti@2.6.1)
@@ -135,15 +138,12 @@ importers:
'@tiny-design/react':
specifier: workspace:*
version: link:../../packages/react
- '@tiny-design/tokens':
- specifier: workspace:*
- version: link:../../packages/tokens
classnames:
specifier: ^2.5.0
version: 2.5.1
next:
specifier: ^15.3.4
- version: 15.5.14(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.98.0)
+ version: 15.5.14(@playwright/test@1.59.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.98.0)
prism-react-renderer:
specifier: ^2.3.0
version: 2.4.1(react@18.3.1)
@@ -202,7 +202,7 @@ importers:
version: 18.3.7(@types/react@18.3.28)
jest:
specifier: ^29.0.0
- version: 29.7.0(@types/node@25.4.0)
+ version: 29.7.0(@types/node@22.19.15)
jest-environment-jsdom:
specifier: ^29.0.0
version: 29.7.0
@@ -220,7 +220,7 @@ importers:
version: 3.0.2
ts-jest:
specifier: ^29.0.0
- version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@25.4.0))(typescript@5.9.3)
+ version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15))(typescript@5.9.3)
tsdown:
specifier: ^0.21.1
version: 0.21.2(typescript@5.9.3)
@@ -275,7 +275,7 @@ importers:
version: 18.3.28
jest:
specifier: ^29.0.0
- version: 29.7.0(@types/node@22.19.15)
+ version: 29.7.0(@types/node@25.4.0)
jest-environment-jsdom:
specifier: ^29.0.0
version: 29.7.0
@@ -290,7 +290,7 @@ importers:
version: 3.0.2
ts-jest:
specifier: ^29.0.0
- version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15))(typescript@5.9.3)
+ version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@25.4.0))(typescript@5.9.3)
tsdown:
specifier: ^0.21.1
version: 0.21.2(typescript@5.9.3)
@@ -401,18 +401,9 @@ importers:
packages/tokens:
devDependencies:
- autoprefixer:
- specifier: ^10.4.4
- version: 10.4.27(postcss@8.5.8)
- postcss:
- specifier: ^8.4.0
- version: 8.5.8
rimraf:
specifier: ^3.0.2
version: 3.0.2
- sass:
- specifier: ^1.49.9
- version: 1.98.0
packages:
@@ -1550,6 +1541,11 @@ packages:
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
+ '@playwright/test@1.59.1':
+ resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
@@ -3533,6 +3529,11 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -5108,6 +5109,16 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+ playwright-core@1.59.1:
+ resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.59.1:
+ resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -7729,6 +7740,10 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.6
optional: true
+ '@playwright/test@1.59.1':
+ dependencies:
+ playwright: 1.59.1
+
'@popperjs/core@2.11.8': {}
'@quansync/fs@1.0.0':
@@ -9873,6 +9888,9 @@ snapshots:
fs.realpath@1.0.0: {}
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -11703,7 +11721,7 @@ snapshots:
neo-async@2.6.2: {}
- next@15.5.14(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.98.0):
+ next@15.5.14(@playwright/test@1.59.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.98.0):
dependencies:
'@next/env': 15.5.14
'@swc/helpers': 0.5.15
@@ -11711,7 +11729,7 @@ snapshots:
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- styled-jsx: 5.1.6(@babel/core@7.29.0)(react@18.3.1)
+ styled-jsx: 5.1.6(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 15.5.14
'@next/swc-darwin-x64': 15.5.14
@@ -11721,6 +11739,7 @@ snapshots:
'@next/swc-linux-x64-musl': 15.5.14
'@next/swc-win32-arm64-msvc': 15.5.14
'@next/swc-win32-x64-msvc': 15.5.14
+ '@playwright/test': 1.59.1
sass: 1.98.0
sharp: 0.34.5
transitivePeerDependencies:
@@ -12051,6 +12070,14 @@ snapshots:
mlly: 1.8.2
pathe: 2.0.3
+ playwright-core@1.59.1: {}
+
+ playwright@1.59.1:
+ dependencies:
+ playwright-core: 1.59.1
+ optionalDependencies:
+ fsevents: 2.3.2
+
possible-typed-array-names@1.1.0: {}
postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.2):
@@ -13051,12 +13078,10 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.29.0)(react@18.3.1):
+ styled-jsx@5.1.6(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
- optionalDependencies:
- '@babel/core': 7.29.0
stylelint-config-recommended-scss@14.1.0(postcss@8.5.8)(stylelint@16.26.1(typescript@5.9.3)):
dependencies: