Skip to content

Commit 1989f69

Browse files
committed
fix: date picker style
1 parent e4eb357 commit 1989f69

15 files changed

Lines changed: 520 additions & 104 deletions

File tree

apps/docs/src/containers/theme-studio/theme-document-adapter.ts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,25 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
203203
'date-picker.header-border': fields.border,
204204
'date-picker.header-button-color': fields.mutedForeground,
205205
'date-picker.header-button-color-hover': fields.primary,
206+
'date-picker.header-action-hover-bg': fields.muted,
207+
'date-picker.header-action-focus-ring': fields.ring,
206208
'date-picker.today-color': fields.primary,
207209
'date-picker.today-color-hover': fields.primary,
210+
'date-picker.today-hover-bg': fields.accent,
211+
'date-picker.today-active-bg': fields.muted,
212+
'date-picker.today-focus-ring': fields.ring,
208213
'picker.cell-hover-bg': fields.muted,
209214
'date-picker.cell-selected-bg': fields.primary,
210215
'date-picker.cell-selected-color': fields.primaryForeground,
211216
'date-picker.cell-selected-hover-bg': fields.primary,
212217
'date-picker.cell-today-border': fields.primary,
213218
'date-picker.cell-radius': fields.inputRadius,
219+
'date-picker.panel-item-hover-bg': fields.muted,
220+
'date-picker.panel-item-current-color': fields.primary,
221+
'date-picker.panel-item-current-border': fields.primary,
222+
'date-picker.panel-item-selected-bg': fields.accent,
223+
'date-picker.panel-item-selected-color': fields.primary,
224+
'date-picker.panel-item-selected-hover-bg': fields.muted,
214225
'date-picker.range-bg': fields.accent,
215226
'time-picker.column-border': fields.border,
216227
'time-picker.cell-bg-selected': fields.primary,
@@ -222,11 +233,15 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
222233
'calendar.bg': fields.card,
223234
'calendar.border': fields.border,
224235
'calendar.radius': fields.cardRadius,
225-
'calendar.hover': fields.muted,
226-
'calendar.nav-button-color': fields.mutedForeground,
227-
'calendar.nav-button-color-hover': fields.primary,
228-
'calendar.title-color': fields.cardForeground,
229-
'calendar.title-color-hover': fields.primary,
236+
'calendar.cell-hover-bg': fields.muted,
237+
'calendar.header-action-color': fields.mutedForeground,
238+
'calendar.header-action-color-hover': fields.primary,
239+
'calendar.header-action-hover-bg': fields.muted,
240+
'calendar.header-action-focus-ring': fields.ring,
241+
'calendar.header-label-color': fields.cardForeground,
242+
'calendar.header-label-color-hover': fields.primary,
243+
'calendar.header-label-hover-bg': fields.muted,
244+
'calendar.header-label-focus-ring': fields.ring,
230245
'calendar.cell-header-color': fields.mutedForeground,
231246
'calendar.week-number-color': fields.mutedForeground,
232247
'calendar.cell-color': fields.cardForeground,
@@ -238,10 +253,16 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum
238253
'calendar.range-bg': fields.accent,
239254
'calendar.cell-focus-outline': fields.shadowFocus,
240255
'calendar.cell-dot-color': fields.primary,
241-
'calendar.panel-cell-radius': fields.inputRadius,
242-
'calendar.panel-cell-color-selected': fields.primaryForeground,
243-
'calendar.panel-cell-bg-selected': fields.primary,
244-
'calendar.today-link-color': fields.primary,
256+
'calendar.panel-item-radius': fields.inputRadius,
257+
'calendar.panel-item-hover-bg': fields.muted,
258+
'calendar.panel-item-selected-color': fields.primaryForeground,
259+
'calendar.panel-item-selected-bg': fields.primary,
260+
'calendar.panel-item-selected-hover-bg': fields.primary,
261+
'calendar.today-color': fields.primary,
262+
'calendar.today-color-hover': fields.primary,
263+
'calendar.today-hover-bg': fields.accent,
264+
'calendar.today-active-bg': fields.muted,
265+
'calendar.today-focus-ring': fields.ring,
245266
'cascader.height.sm': fields.fieldHeightSm,
246267
'cascader.height.md': fields.fieldHeightMd,
247268
'cascader.height.lg': fields.fieldHeightLg,

packages/mcp/src/data/components.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5764,6 +5764,14 @@
57645764
"name": "Basic",
57655765
"code": "import React from 'react';\nimport { Segmented } from '@tiny-design/react';\n\nexport default function BasicDemo() {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>\n <Segmented\n options={[\n { label: 'Daily', value: 'daily' },\n { label: 'Weekly', value: 'weekly' },\n { label: 'Monthly', value: 'monthly' },\n { label: 'Yearly', value: 'yearly' },\n ]}\n />\n <Segmented\n options={[\n { label: 'Small', value: 'sm' },\n { label: 'Medium', value: 'md' },\n { label: 'Large', value: 'lg' },\n ]}\n size=\"sm\"\n />\n <Segmented\n options={[\n { label: 'Map', value: 'map' },\n { label: 'Transit', value: 'transit' },\n { label: 'Satellite', value: 'satellite' },\n ]}\n block\n />\n </div>\n );\n}\n"
57665766
},
5767+
{
5768+
"name": "Controlled",
5769+
"code": "import React from 'react';\nimport { Segmented, Text } from '@tiny-design/react';\n\nconst options = [\n { label: 'List', value: 'list' },\n { label: 'Board', value: 'board' },\n { label: 'Timeline', value: 'timeline' },\n];\n\nexport default function ControlledDemo() {\n const [value, setValue] = React.useState('list');\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <Segmented\n options={options}\n value={value}\n onChange={(nextValue) => setValue(String(nextValue))}\n />\n <Text type=\"secondary\">Current value: {value}</Text>\n </div>\n );\n}\n"
5770+
},
5771+
{
5772+
"name": "DefaultValue",
5773+
"code": "import React from 'react';\nimport { Segmented, Text } from '@tiny-design/react';\n\nexport default function DefaultValueDemo() {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <Segmented\n options={[\n { label: 'Draft', value: 'draft' },\n { label: 'Review', value: 'review' },\n { label: 'Published', value: 'published' },\n ]}\n defaultValue=\"review\"\n />\n <Text type=\"secondary\">Initial selection is set with defaultValue.</Text>\n </div>\n );\n}\n"
5774+
},
57675775
{
57685776
"name": "Disabled",
57695777
"code": "import React from 'react';\nimport { Segmented } from '@tiny-design/react';\n\nexport default function DisabledDemo() {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>\n <Segmented\n options={[\n { label: 'Daily', value: 'daily' },\n { label: 'Weekly', value: 'weekly' },\n { label: 'Monthly', value: 'monthly' },\n ]}\n disabled\n />\n <Segmented\n options={[\n { label: 'Active', value: 'active' },\n { label: 'Disabled', value: 'disabled', disabled: true },\n { label: 'Active', value: 'active2' },\n ]}\n />\n </div>\n );\n}\n"
@@ -5772,6 +5780,10 @@
57725780
"name": "Icon",
57735781
"code": "import React from 'react';\nimport { Segmented } from '@tiny-design/react';\nimport { IconUser, IconStar, IconWifi } from '@tiny-design/icons';\n\nexport default function IconDemo() {\n return (\n <Segmented\n options={[\n { label: 'User', value: 'user', icon: <IconUser /> },\n { label: 'Star', value: 'star', icon: <IconStar /> },\n { label: 'Wifi', value: 'wifi', icon: <IconWifi /> },\n ]}\n />\n );\n}"
57745782
},
5783+
{
5784+
"name": "NoSelection",
5785+
"code": "import React from 'react';\nimport { Segmented, Text } from '@tiny-design/react';\n\nexport default function NoSelectionDemo() {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <Segmented\n options={[\n { label: 'All', value: 'all' },\n { label: 'Open', value: 'open' },\n { label: 'Closed', value: 'closed' },\n ]}\n />\n <Text type=\"secondary\">Without value or defaultValue, no option is selected initially.</Text>\n </div>\n );\n}\n"
5786+
},
57755787
{
57765788
"name": "Size",
57775789
"code": "import React from 'react';\nimport { Segmented } from '@tiny-design/react';\n\nexport default function SizeDemo() {\n const options = [\n { label: 'Small', value: 'sm' },\n { label: 'Medium', value: 'md' },\n { label: 'Large', value: 'lg' },\n ];\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>\n <Segmented options={options} size=\"sm\" />\n <Segmented options={options} size=\"md\" />\n <Segmented options={options} size=\"lg\" />\n </div>\n );\n}\n"

packages/react/src/calendar/index.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ Display a full calendar view with date selection. Useful for scheduling, event m
2727
import { Calendar } from 'tiny-design';
2828
```
2929

30+
## Design Tokens
31+
32+
`Calendar` follows the same semantic token structure as `DatePicker`, but keeps its own component namespace:
33+
34+
- `calendar.header-*`: header spacing, navigation actions, caption labels, and focus states
35+
- `calendar.cell-*`: month grid cells, weekday headers, week numbers, selected dates, and range states
36+
- `calendar.panel-item-*`: year and decade panel items for hover and selected states
37+
- `calendar.today-*`: footer "Today" action typography and interaction states
38+
39+
This alignment is intentional: `Calendar` and `DatePicker` should feel related, but token keys stay separate so product teams can tune them independently.
40+
3041
## Examples
3142

3243
<Layout>
@@ -160,4 +171,4 @@ Compact card calendar with keyboard navigation. Use arrow keys, Enter, and Escap
160171
|| Move focus to previous week |
161172
|| Move focus to next week |
162173
| Enter | Select the focused date |
163-
| Escape | Reset focus |
174+
| Escape | Reset focus |

packages/react/src/calendar/index.zh_CN.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ import CardModeSource from './demo/CardMode.tsx?raw';
2727
import { Calendar } from 'tiny-design';
2828
```
2929

30+
## Design Tokens
31+
32+
`Calendar``DatePicker` 使用同构的 token 语义结构,但保留独立的组件命名空间:
33+
34+
- `calendar.header-*`:头部间距、导航操作、标题标签与焦点状态
35+
- `calendar.cell-*`:月份网格单元、星期标题、周数、选中日期和范围状态
36+
- `calendar.panel-item-*`:年份 / 十年面板项的 hover 与选中状态
37+
- `calendar.today-*`:页脚 “Today / 今天” 操作的字体与交互状态
38+
39+
这种设计是刻意的:`Calendar``DatePicker` 在视觉语义上应当保持一致,但 token key 不共享,这样业务侧可以分别微调两个组件。
40+
3041
## 代码演示
3142

3243
<Layout>
@@ -160,4 +171,4 @@ import { Calendar } from 'tiny-design';
160171
|| 移动到上一周 |
161172
|| 移动到下一周 |
162173
| Enter | 选择当前焦点日期 |
163-
| Escape | 重置焦点 |
174+
| Escape | 重置焦点 |

packages/react/src/calendar/style/index.scss

Lines changed: 84 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,27 @@
3333
display: inline-flex;
3434
align-items: center;
3535
justify-content: center;
36-
width: var(--ty-calendar-nav-button-size);
37-
height: var(--ty-calendar-nav-button-size);
36+
width: var(--ty-calendar-header-action-size);
37+
height: var(--ty-calendar-header-action-size);
3838
border: none;
3939
background: transparent;
4040
cursor: pointer;
41-
border-radius: var(--ty-calendar-nav-button-radius);
42-
font-size: var(--ty-calendar-nav-button-font-size);
43-
color: var(--ty-calendar-nav-button-color);
44-
transition: color 0.2s;
41+
border-radius: var(--ty-calendar-header-action-radius);
42+
font-size: var(--ty-calendar-header-action-font-size);
43+
color: var(--ty-calendar-header-action-color);
44+
transition:
45+
color 0.2s,
46+
background-color 0.2s,
47+
box-shadow 0.2s;
4548

4649
&:hover:not(&_disabled) {
47-
color: var(--ty-calendar-nav-button-color-hover);
50+
color: var(--ty-calendar-header-action-color-hover);
51+
background: var(--ty-calendar-header-action-hover-bg);
52+
}
53+
54+
&:focus-visible {
55+
outline: none;
56+
box-shadow: inset 0 0 0 2px var(--ty-calendar-header-action-focus-ring);
4857
}
4958

5059
&_disabled {
@@ -56,24 +65,39 @@
5665
&__title {
5766
display: flex;
5867
align-items: center;
59-
gap: var(--ty-calendar-title-gap);
60-
font-weight: var(--ty-calendar-title-font-weight);
61-
font-size: var(--ty-calendar-title-font-size);
68+
gap: var(--ty-calendar-header-caption-gap);
69+
font-weight: var(--ty-calendar-header-label-font-weight);
70+
font-size: var(--ty-calendar-header-label-font-size);
6271
}
6372

6473
&__title-btn {
74+
display: inline-flex;
75+
align-items: center;
76+
justify-content: center;
6577
border: none;
6678
background: transparent;
6779
cursor: pointer;
68-
font-weight: var(--ty-calendar-title-font-weight);
80+
min-height: var(--ty-calendar-header-label-min-height);
81+
padding: 0 var(--ty-calendar-header-label-padding-inline);
82+
font-weight: var(--ty-calendar-header-label-font-weight);
6983
font-size: inherit;
70-
padding: var(--ty-calendar-title-button-padding);
71-
border-radius: var(--ty-calendar-title-button-radius);
72-
color: var(--ty-calendar-title-color);
73-
transition: color 0.2s;
84+
font-family: inherit;
85+
line-height: 1;
86+
border-radius: var(--ty-calendar-header-label-radius);
87+
color: var(--ty-calendar-header-label-color);
88+
transition:
89+
color 0.2s,
90+
background-color 0.2s,
91+
box-shadow 0.2s;
7492

7593
&:hover {
76-
color: var(--ty-calendar-title-color-hover);
94+
color: var(--ty-calendar-header-label-color-hover);
95+
background: var(--ty-calendar-header-label-hover-bg);
96+
}
97+
98+
&:focus-visible {
99+
outline: none;
100+
box-shadow: inset 0 0 0 2px var(--ty-calendar-header-label-focus-ring);
77101
}
78102
}
79103

@@ -134,7 +158,7 @@
134158
}
135159

136160
&:not(&_disabled):hover .#{$prefix}-calendar__cell-inner {
137-
background: var(--ty-calendar-hover);
161+
background: var(--ty-calendar-cell-hover-bg);
138162
}
139163

140164
&_selected:not(&_disabled):hover .#{$prefix}-calendar__cell-inner {
@@ -264,21 +288,22 @@
264288

265289
&__month-cell {
266290
text-align: center;
267-
padding: var(--ty-calendar-panel-cell-padding);
291+
min-height: var(--ty-calendar-panel-item-min-height);
292+
padding: 16px var(--ty-calendar-panel-item-padding-inline);
268293
cursor: pointer;
269-
border-radius: var(--ty-calendar-panel-cell-radius);
294+
border-radius: var(--ty-calendar-panel-item-radius);
270295
transition: all 0.2s;
271296

272297
&:hover {
273-
background: var(--ty-calendar-hover);
298+
background: var(--ty-calendar-panel-item-hover-bg);
274299
}
275300

276301
&_selected {
277-
background: var(--ty-calendar-panel-cell-bg-selected);
278-
color: var(--ty-calendar-panel-cell-color-selected);
302+
background: var(--ty-calendar-panel-item-selected-bg);
303+
color: var(--ty-calendar-panel-item-selected-color);
279304

280305
&:hover {
281-
background: var(--ty-calendar-panel-cell-bg-selected-hover);
306+
background: var(--ty-calendar-panel-item-selected-hover-bg);
282307
}
283308
}
284309
}
@@ -305,22 +330,23 @@
305330

306331
&__decade-cell {
307332
text-align: center;
308-
padding: var(--ty-calendar-panel-cell-padding);
333+
min-height: var(--ty-calendar-panel-item-min-height);
334+
padding: 16px var(--ty-calendar-panel-item-padding-inline);
309335
cursor: pointer;
310-
border-radius: var(--ty-calendar-panel-cell-radius);
336+
border-radius: var(--ty-calendar-panel-item-radius);
311337
transition: all 0.2s;
312338
font-size: var(--ty-calendar-decade-font-size);
313339

314340
&:hover {
315-
background: var(--ty-calendar-hover);
341+
background: var(--ty-calendar-panel-item-hover-bg);
316342
}
317343

318344
&_selected {
319-
background: var(--ty-calendar-panel-cell-bg-selected);
320-
color: var(--ty-calendar-panel-cell-color-selected);
345+
background: var(--ty-calendar-panel-item-selected-bg);
346+
color: var(--ty-calendar-panel-item-selected-color);
321347

322348
&:hover {
323-
background: var(--ty-calendar-panel-cell-bg-selected-hover);
349+
background: var(--ty-calendar-panel-item-selected-hover-bg);
324350
}
325351
}
326352

@@ -338,12 +364,38 @@
338364
}
339365

340366
&__today-link {
341-
color: var(--ty-calendar-today-link-color);
367+
display: inline-flex;
368+
align-items: center;
369+
justify-content: center;
370+
min-height: 28px;
371+
padding: 0 12px;
372+
border: 1px solid transparent;
373+
border-radius: var(--ty-calendar-header-label-radius);
374+
background: transparent;
375+
color: var(--ty-calendar-today-color);
342376
cursor: pointer;
343-
font-size: var(--ty-calendar-today-link-font-size);
377+
font: inherit;
378+
font-size: var(--ty-calendar-today-font-size);
379+
line-height: 1;
380+
appearance: none;
381+
transition:
382+
color 0.2s,
383+
background-color 0.2s,
384+
border-color 0.2s,
385+
box-shadow 0.2s;
344386

345387
&:hover {
346-
color: var(--ty-calendar-today-link-color-hover);
388+
color: var(--ty-calendar-today-color-hover);
389+
background: var(--ty-calendar-today-hover-bg);
390+
}
391+
392+
&:active {
393+
background: var(--ty-calendar-today-active-bg);
394+
}
395+
396+
&:focus-visible {
397+
outline: none;
398+
box-shadow: inset 0 0 0 2px var(--ty-calendar-today-focus-ring);
347399
}
348400
}
349401
}

packages/react/src/date-picker/index.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ To select or input a date.
2525
import { DatePicker } from 'tiny-design';
2626
```
2727

28+
## Design Tokens
29+
30+
`DatePicker` uses component-scoped tokens with a small set of stable semantic groups:
31+
32+
- `date-picker.header-*`: header layout, navigation buttons, caption spacing, and interactive header states
33+
- `date-picker.cell-*`: date grid cells, selected dates, muted dates, and range states
34+
- `date-picker.panel-item-*`: month/year panel items for hover, current item, and selected item styling
35+
- `date-picker.today-*`: footer "Today" action typography and interaction states
36+
37+
`DatePicker` intentionally does not share token keys with `Calendar`. The two components align on token semantics, but keep separate namespaces so each component can evolve independently.
38+
2839
## Examples
2940

3041
<Layout>

packages/react/src/date-picker/index.zh_CN.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ import RangeDisabledSource from './demo/RangeDisabled.tsx?raw';
2525
import { DatePicker } from 'tiny-design';
2626
```
2727

28+
## Design Tokens
29+
30+
`DatePicker` 使用组件级 token,并按稳定的语义分组组织:
31+
32+
- `date-picker.header-*`:头部布局、翻页按钮、标题间距以及头部交互状态
33+
- `date-picker.cell-*`:日期网格单元、选中态、弱化态和范围选择状态
34+
- `date-picker.panel-item-*`:月份 / 年份面板项的 hover、当前项和选中项样式
35+
- `date-picker.today-*`:页脚 “Today / 今天” 操作的字体与交互状态
36+
37+
`DatePicker` 不会与 `Calendar` 共享同一个 token key。两个组件只对齐语义结构,不共用命名空间,这样可以保证后续样式演进彼此独立。
38+
2839
## 代码示例
2940

3041
<Layout>

0 commit comments

Comments
 (0)