@mindlogic-ai/logician-ui 5.0.0-alpha.11 โ†’ 5.0.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +1 -0
  2. package/dist/components/Input/Input.d.ts.map +1 -1
  3. package/dist/components/Input/Input.js +4 -0
  4. package/dist/components/Input/Input.js.map +1 -1
  5. package/dist/components/Input/Input.mjs +4 -0
  6. package/dist/components/Input/Input.mjs.map +1 -1
  7. package/dist/components/NavList/NavList.d.ts +34 -0
  8. package/dist/components/NavList/NavList.d.ts.map +1 -0
  9. package/dist/components/NavList/NavList.js +113 -0
  10. package/dist/components/NavList/NavList.js.map +1 -0
  11. package/dist/components/NavList/NavList.mjs +111 -0
  12. package/dist/components/NavList/NavList.mjs.map +1 -0
  13. package/dist/components/NavList/NavList.styles.d.ts +90 -0
  14. package/dist/components/NavList/NavList.styles.d.ts.map +1 -0
  15. package/dist/components/NavList/NavList.styles.js +104 -0
  16. package/dist/components/NavList/NavList.styles.js.map +1 -0
  17. package/dist/components/NavList/NavList.styles.mjs +97 -0
  18. package/dist/components/NavList/NavList.styles.mjs.map +1 -0
  19. package/dist/components/NavList/NavList.translations.json.js +19 -0
  20. package/dist/components/NavList/NavList.translations.json.js.map +1 -0
  21. package/dist/components/NavList/NavList.translations.json.mjs +14 -0
  22. package/dist/components/NavList/NavList.translations.json.mjs.map +1 -0
  23. package/dist/components/NavList/NavList.types.d.ts +131 -0
  24. package/dist/components/NavList/NavList.types.d.ts.map +1 -0
  25. package/dist/components/NavList/NavListContext.d.ts +30 -0
  26. package/dist/components/NavList/NavListContext.d.ts.map +1 -0
  27. package/dist/components/NavList/NavListContext.js +16 -0
  28. package/dist/components/NavList/NavListContext.js.map +1 -0
  29. package/dist/components/NavList/NavListContext.mjs +13 -0
  30. package/dist/components/NavList/NavListContext.mjs.map +1 -0
  31. package/dist/components/NavList/NavListGroup.d.ts +31 -0
  32. package/dist/components/NavList/NavListGroup.d.ts.map +1 -0
  33. package/dist/components/NavList/NavListGroup.js +115 -0
  34. package/dist/components/NavList/NavListGroup.js.map +1 -0
  35. package/dist/components/NavList/NavListGroup.mjs +113 -0
  36. package/dist/components/NavList/NavListGroup.mjs.map +1 -0
  37. package/dist/components/NavList/NavListItem.d.ts +15 -0
  38. package/dist/components/NavList/NavListItem.d.ts.map +1 -0
  39. package/dist/components/NavList/NavListItem.js +269 -0
  40. package/dist/components/NavList/NavListItem.js.map +1 -0
  41. package/dist/components/NavList/NavListItem.mjs +267 -0
  42. package/dist/components/NavList/NavListItem.mjs.map +1 -0
  43. package/dist/components/NavList/NavListSkeleton.d.ts +17 -0
  44. package/dist/components/NavList/NavListSkeleton.d.ts.map +1 -0
  45. package/dist/components/NavList/NavListSkeleton.js +33 -0
  46. package/dist/components/NavList/NavListSkeleton.js.map +1 -0
  47. package/dist/components/NavList/NavListSkeleton.mjs +31 -0
  48. package/dist/components/NavList/NavListSkeleton.mjs.map +1 -0
  49. package/dist/components/NavList/index.d.ts +7 -0
  50. package/dist/components/NavList/index.d.ts.map +1 -0
  51. package/dist/components/Textarea/Textarea.d.ts.map +1 -1
  52. package/dist/components/Textarea/Textarea.js +4 -0
  53. package/dist/components/Textarea/Textarea.js.map +1 -1
  54. package/dist/components/Textarea/Textarea.mjs +4 -0
  55. package/dist/components/Textarea/Textarea.mjs.map +1 -1
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +12 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/index.mjs +6 -1
  61. package/dist/index.mjs.map +1 -1
  62. package/dist/utils/focusRing.d.ts +22 -0
  63. package/dist/utils/focusRing.d.ts.map +1 -1
  64. package/dist/utils/focusRing.js +22 -0
  65. package/dist/utils/focusRing.js.map +1 -1
  66. package/dist/utils/focusRing.mjs +21 -1
  67. package/dist/utils/focusRing.mjs.map +1 -1
  68. package/package.json +2 -1
  69. package/src/components/Input/Input.tsx +4 -0
  70. package/src/components/Menu/Menu.usage.mdx +8 -0
  71. package/src/components/NavList/NavList.stories.tsx +895 -0
  72. package/src/components/NavList/NavList.styles.ts +114 -0
  73. package/src/components/NavList/NavList.test.tsx +1254 -0
  74. package/src/components/NavList/NavList.translations.json +9 -0
  75. package/src/components/NavList/NavList.tsx +160 -0
  76. package/src/components/NavList/NavList.types.ts +145 -0
  77. package/src/components/NavList/NavList.usage.mdx +540 -0
  78. package/src/components/NavList/NavListContext.ts +25 -0
  79. package/src/components/NavList/NavListGroup.tsx +243 -0
  80. package/src/components/NavList/NavListItem.tsx +525 -0
  81. package/src/components/NavList/NavListSkeleton.tsx +57 -0
  82. package/src/components/NavList/index.ts +14 -0
  83. package/src/components/Tabs/Tabs.usage.mdx +58 -14
  84. package/src/components/Textarea/Textarea.tsx +4 -0
  85. package/src/components/Tooltip/Tooltip.usage.mdx +8 -0
  86. package/src/components/a11y.kwcag.test.tsx +66 -0
  87. package/src/components/claude.md +1 -1
  88. package/src/index.ts +7 -1
  89. package/src/utils/focusRing.ts +23 -0
@@ -25,16 +25,35 @@ One section shows at a time.
25
25
  ## When not to use
26
26
 
27
27
  - To change how the same content is shown, or to filter it (์ผ/์ฃผ/์›”, ๋ชฉ๋ก/์นด๋“œ)
28
- โ†’ `SegmentedControl`. Tabs change *what* you are looking at, not how.
28
+ โ†’ `SegmentedControl`. Tabs change _what_ you are looking at, not how.
29
29
  - For the steps of a flow the user must complete in order โ†’ a stepper or a
30
30
  sequence of pages. Tabs let the user jump ahead.
31
- - To move to another page of the app โ†’ navigation links or `Breadcrumb`.
31
+ - For destinations or filters, such as the side navigation of an area or a
32
+ filter rail beside a list, and for a list that is long or keeps growing, or
33
+ whose rows carry counts or icons โ†’ `NavList`. Tabs own their panels, move
34
+ with the arrow keys and mark the choice with `aria-selected`; a rail is
35
+ links or filters, reached with Tab.
36
+ - To show where this page sits in a hierarchy โ†’ `Breadcrumb`.
32
37
  - For two or three short panels the user needs side by side โ†’ show them on
33
38
  the page.
34
39
 
40
+ ## Which component
41
+
42
+ Tabs and `NavList` both mark one row as current; they say the same thing from
43
+ opposite sides. Pick by what the rows are:
44
+
45
+ | What you have | Use |
46
+ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
47
+ | A few sections of one object that swap a panel in place | `Tabs` |
48
+ | Destinations, or the side navigation of an area | `NavList`, each item with `href` |
49
+ | A filter over one list or grid, with many options or with counts | `NavList`: links (`href`) when the filter state is in the URL, buttons (`onClick`) otherwise |
50
+ | A short horizontal switch of two to five options | `SegmentedControl` |
51
+ | A hierarchy the user expands and collapses | `Tree` |
52
+ | Commands (์ดˆ๋Œ€ํ•˜๊ธฐ, ๋‚ด๋ณด๋‚ด๊ธฐ) | `Button`, or a `Menu` when there are several |
53
+
35
54
  ## Variants and what they mean
36
55
 
37
- Tabs has one look. A selected tab is a *place*, not a value, so it is marked
56
+ Tabs has one look. A selected tab is a _place_, not a value, so it is marked
38
57
  with ink and weight, not with a brand or status colour. `orientation` decides
39
58
  the layout:
40
59
 
@@ -59,9 +78,27 @@ the layout:
59
78
  ์”๋‹ˆ๋‹ค. ํƒญ์€ ๋ฌด์—‡์„ ๋ณด๋Š”์ง€๋ฅผ ๋ฐ”๊พธ๊ณ , ์–ด๋–ป๊ฒŒ ๋ณด๋Š”์ง€๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
60
79
  - ์ฐจ๋ก€๋Œ€๋กœ ๋๋‚ด์•ผ ํ•˜๋Š” ํ๋ฆ„์˜ ๋‹จ๊ณ„์—๋Š” ๋‹จ๊ณ„ ํ‘œ์‹œ๋‚˜ ์ด์–ด์ง€๋Š” ํŽ˜์ด์ง€๋ฅผ ์”๋‹ˆ๋‹ค. ํƒญ์€ ์‚ฌ์šฉ์ž๊ฐ€
61
80
  ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋‘ก๋‹ˆ๋‹ค.
62
- - ์•ฑ์˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์˜ฎ๊ฒจ ๊ฐ€๋Š” ๊ฒƒ์€ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋งํฌ๋‚˜ `Breadcrumb`๊ฐ€ ๋งก์Šต๋‹ˆ๋‹ค.
81
+ - ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ฒจ ๊ฐ€๋Š” ํ•ญ๋ชฉ์ด๋‚˜ ํ•„ํ„ฐ(ํ•œ ์˜์—ญ์˜ ์˜† ๋‚ด๋น„๊ฒŒ์ด์…˜, ๋ชฉ๋ก ์˜†์˜ ํ•„ํ„ฐ ๋ ˆ์ผ)๋Š”
82
+ `NavList`๊ฐ€ ๋งก์Šต๋‹ˆ๋‹ค. ํ•ญ๋ชฉ์ด ๋งŽ๊ฑฐ๋‚˜ ๊ณ„์† ๋Š˜์–ด๋‚˜๋Š” ๋ชฉ๋ก, ํ•ญ๋ชฉ์— ๊ฐœ์ˆ˜๋‚˜ ์•„์ด์ฝ˜์ด ๋ถ™๋Š”
83
+ ๋ชฉ๋ก๋„ `NavList`๋ฅผ ์”๋‹ˆ๋‹ค. ํƒญ์€ ํŒจ๋„์„ ๊ฐ–๊ณ , ๋ฐฉํ–ฅํ‚ค๋กœ ์˜ฎ๊ธฐ๋ฉฐ, ๊ณ ๋ฅธ ๊ฒƒ์„
84
+ `aria-selected`๋กœ ์•Œ๋ฆฝ๋‹ˆ๋‹ค. ๋ ˆ์ผ์€ ๋งํฌ๋‚˜ ํ•„ํ„ฐ์ด๊ณ  Tab ํ‚ค๋กœ ์˜ค๊ฐ‘๋‹ˆ๋‹ค.
85
+ - ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ณ„์ธต์˜ ์–ด๋””์— ์žˆ๋Š”์ง€ ๋ณด์—ฌ ์ค„ ๋•Œ๋Š” `Breadcrumb`๋ฅผ ์”๋‹ˆ๋‹ค.
63
86
  - ๋‚˜๋ž€ํžˆ ๋ด์•ผ ํ•˜๋Š” ์งง์€ ํŒจ๋„ ๋‘์„ธ ๊ฐœ๋Š” ํƒญ์— ์ˆจ๊ธฐ์ง€ ๋ง๊ณ  ํŽ˜์ด์ง€์— ๊ทธ๋Œ€๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.
64
87
 
88
+ ## ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์“ฐ๋‚˜์š”
89
+
90
+ Tabs์™€ `NavList`๋Š” ๋‘˜ ๋‹ค ํ•ญ๋ชฉ ํ•˜๋‚˜๋ฅผ ํ˜„์žฌ ํ•ญ๋ชฉ์œผ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฌธ์„œ๋Š” ๊ฐ™์€ ๊ธฐ์ค€์„ ์„œ๋กœ
91
+ ๋ฐ˜๋Œ€์ชฝ์—์„œ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ํ•ญ๋ชฉ์ด ๋ฌด์—‡์ธ์ง€๋ฅผ ๋ณด๊ณ  ๊ณ ๋ฆ…๋‹ˆ๋‹ค.
92
+
93
+ | ์ด๋Ÿฐ ๊ฒฝ์šฐ | ์“ฐ๋Š” ์ปดํฌ๋„ŒํŠธ |
94
+ | -------------------------------------------------------------------- | ------------------------------------------------------------------------ |
95
+ | ํ•œ ๋Œ€์ƒ์˜ ๊ตฌ์—ญ ๋ช‡ ๊ฐœ๋ฅผ ๊ทธ ์ž๋ฆฌ์—์„œ ํŒจ๋„๋กœ ๋ฐ”๊ฟ” ๋ณด์—ฌ ์ค„ ๋•Œ | `Tabs` |
96
+ | ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ฒจ ๊ฐ€๋Š” ํ•ญ๋ชฉ, ํ•œ ์˜์—ญ์˜ ์˜† ๋‚ด๋น„๊ฒŒ์ด์…˜ | ํ•ญ๋ชฉ๋งˆ๋‹ค `href`๋ฅผ ์ค€ `NavList` |
97
+ | ๋ชฉ๋ก์ด๋‚˜ ๊ทธ๋ฆฌ๋“œ ํ•˜๋‚˜๋ฅผ ๊ฑฐ๋ฅด๋Š” ํ•„ํ„ฐ๋กœ, ์„ ํƒ์ง€๊ฐ€ ๋งŽ๊ฑฐ๋‚˜ ๊ฐœ์ˆ˜๋ฅผ ๋ถ™์ผ ๋•Œ | `NavList`. ํ•„ํ„ฐ ์ƒํƒœ๊ฐ€ URL์— ์žˆ์œผ๋ฉด ๋งํฌ(`href`), ์•„๋‹ˆ๋ฉด ๋ฒ„ํŠผ(`onClick`) |
98
+ | ์„ ํƒ์ง€ ๋‘˜์—์„œ ๋‹ค์„ฏ ๊ฐœ๋ฅผ ๊ฐ€๋กœ๋กœ ์งง๊ฒŒ ๋ฐ”๊ฟ€ ๋•Œ | `SegmentedControl` |
99
+ | ํŽผ์น˜๊ณ  ์ ‘๋Š” ๊ณ„์ธต | `Tree` |
100
+ | ๋ช…๋ น(์ดˆ๋Œ€ํ•˜๊ธฐ, ๋‚ด๋ณด๋‚ด๊ธฐ) | `Button`, ์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด `Menu` |
101
+
65
102
  ## ๋ณ€ํ˜•๊ณผ ๋œป
66
103
 
67
104
  Tabs์˜ ๋ชจ์–‘์€ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์„ ํƒ๋œ ํƒญ์€ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์ง€๊ธˆ ์žˆ๋Š” ๊ณณ์ด๋ฏ€๋กœ, ๋ธŒ๋žœ๋“œ์ƒ‰์ด๋‚˜
@@ -69,14 +106,17 @@ Tabs์˜ ๋ชจ์–‘์€ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์„ ํƒ๋œ ํƒญ์€ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์ง€๊ธˆ ์žˆ
69
106
 
70
107
  </Ko>
71
108
 
72
- <ArgTypes of={TabsStories} include={['orientation', 'defaultValue', 'value', 'activationMode']} />
109
+ <ArgTypes
110
+ of={TabsStories}
111
+ include={['orientation', 'defaultValue', 'value', 'activationMode']}
112
+ />
73
113
 
74
114
  <En>
75
115
 
76
- | `orientation` | Selected tab | Use for |
77
- | --- | --- | --- |
78
- | `horizontal` (default) | `fg.emphasized` label, semibold, with a 2px `primary.main` underline | Sections across the top of a page or panel |
79
- | `vertical` | `bg.muted` fill, bold, with a 2px `primary.main` bar on the leading edge | A long settings page in a narrow side column |
116
+ | `orientation` | Selected tab | Use for |
117
+ | ---------------------- | ------------------------------------------------------------------------ | -------------------------------------------- |
118
+ | `horizontal` (default) | `fg.emphasized` label, semibold, with a 2px `primary.main` underline | Sections across the top of a page or panel |
119
+ | `vertical` | `bg.muted` fill, bold, with a 2px `primary.main` bar on the leading edge | A long settings page in a narrow side column |
80
120
 
81
121
  ## Do / Don't
82
122
 
@@ -84,10 +124,10 @@ Tabs์˜ ๋ชจ์–‘์€ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์„ ํƒ๋œ ํƒญ์€ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์ง€๊ธˆ ์žˆ
84
124
 
85
125
  <Ko>
86
126
 
87
- | `orientation` | ์„ ํƒ๋œ ํƒญ | ์“ฐ๋Š” ๊ณณ |
88
- | --- | --- | --- |
89
- | `horizontal` (๊ธฐ๋ณธ) | `fg.emphasized` ๊ธ€์ž๋ฅผ semibold๋กœ ์“ฐ๊ณ  ์•„๋ž˜์— 2px `primary.main` ๋ฐ‘์ค„ | ํŽ˜์ด์ง€๋‚˜ ํŒจ๋„ ์œ„์ชฝ์˜ ๊ตฌ์—ญ |
90
- | `vertical` | `bg.muted` ๋ฐฐ๊ฒฝ์— bold, ์•ž์ชฝ ๊ฐ€์žฅ์ž๋ฆฌ์— 2px `primary.main` ๋ง‰๋Œ€ | ์ข์€ ์˜† ์—ด์— ๋‘” ๊ธด ์„ค์ • ํ™”๋ฉด |
127
+ | `orientation` | ์„ ํƒ๋œ ํƒญ | ์“ฐ๋Š” ๊ณณ |
128
+ | ------------------- | --------------------------------------------------------------------- | ---------------------------- |
129
+ | `horizontal` (๊ธฐ๋ณธ) | `fg.emphasized` ๊ธ€์ž๋ฅผ semibold๋กœ ์“ฐ๊ณ  ์•„๋ž˜์— 2px `primary.main` ๋ฐ‘์ค„ | ํŽ˜์ด์ง€๋‚˜ ํŒจ๋„ ์œ„์ชฝ์˜ ๊ตฌ์—ญ |
130
+ | `vertical` | `bg.muted` ๋ฐฐ๊ฒฝ์— bold, ์•ž์ชฝ ๊ฐ€์žฅ์ž๋ฆฌ์— 2px `primary.main` ๋ง‰๋Œ€ | ์ข์€ ์˜† ์—ด์— ๋‘” ๊ธด ์„ค์ • ํ™”๋ฉด |
91
131
 
92
132
  ## ์ด๋ ‡๊ฒŒ ํ•˜์„ธ์š” / ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋งˆ์„ธ์š”
93
133
 
@@ -116,7 +156,7 @@ Tabs์˜ ๋ชจ์–‘์€ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์„ ํƒ๋œ ํƒญ์€ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์ง€๊ธˆ ์žˆ
116
156
  - Keep labels the same length class; one long label next to short ones reads as
117
157
  a different kind of tab.
118
158
  - Stay under about six horizontal tabs. More than that, use `vertical` or split
119
- the page.
159
+ the page; if the rows are places or filters, use `NavList`.
120
160
  - Do not put a count in the label unless it changes what the user does next.
121
161
 
122
162
  ## Accessibility
@@ -162,6 +202,7 @@ The selected ramp is `TAB_RAMP` in `Tab.styles.ts`; the other tokens are set in
162
202
  - ๋ผ๋ฒจ ๊ธธ์ด๋ฅผ ๋น„์Šทํ•˜๊ฒŒ ๋งž์ถฅ๋‹ˆ๋‹ค. ์งง์€ ๋ผ๋ฒจ ์‚ฌ์ด์˜ ๊ธด ๋ผ๋ฒจ ํ•˜๋‚˜๋Š” ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ํƒญ์ฒ˜๋Ÿผ
163
203
  ๋ณด์ž…๋‹ˆ๋‹ค.
164
204
  - ๊ฐ€๋กœ ํƒญ์€ ์—ฌ์„ฏ ๊ฐœ ์•ˆ์ชฝ์œผ๋กœ ๋‘ก๋‹ˆ๋‹ค. ๋” ๋งŽ์œผ๋ฉด `vertical`์„ ์“ฐ๊ฑฐ๋‚˜ ํŽ˜์ด์ง€๋ฅผ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.
205
+ ํ•ญ๋ชฉ์ด ๊ณณ์ด๋‚˜ ํ•„ํ„ฐ๋ผ๋ฉด `NavList`๋ฅผ ์”๋‹ˆ๋‹ค.
165
206
  - ๋‹ค์Œ ํ–‰๋™์„ ๋ฐ”๊พธ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ผ๋ฒจ์— ๊ฐœ์ˆ˜๋ฅผ ๋„ฃ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
166
207
 
167
208
  ## ์ ‘๊ทผ์„ฑ
@@ -200,6 +241,8 @@ Tabs๊ฐ€ ์•Œ์•„์„œ ํ•˜๋Š” ์ผ
200
241
 
201
242
  ## Related
202
243
 
244
+ - **NavList**: destinations and filters, such as side navigation and filter
245
+ rails.
203
246
  - **SegmentedControl**: change how the same content is shown.
204
247
  - **Breadcrumb**: where this page sits in a hierarchy.
205
248
  - **Foundations/Brand & colour usage**: why a selected place is ink, not
@@ -211,6 +254,7 @@ Tabs๊ฐ€ ์•Œ์•„์„œ ํ•˜๋Š” ์ผ
211
254
 
212
255
  ## ๊ด€๋ จ ์ปดํฌ๋„ŒํŠธ
213
256
 
257
+ - **NavList**: ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์˜ฎ๊ฒจ ๊ฐ€๋Š” ํ•ญ๋ชฉ๊ณผ ํ•„ํ„ฐ. ์˜† ๋‚ด๋น„๊ฒŒ์ด์…˜, ํ•„ํ„ฐ ๋ ˆ์ผ.
214
258
  - **SegmentedControl**: ๊ฐ™์€ ๋‚ด์šฉ์„ ๋ณด๋Š” ๋ฐฉ์‹ ๋ฐ”๊พธ๊ธฐ.
215
259
  - **Breadcrumb**: ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ณ„์ธต์˜ ์–ด๋””์— ์žˆ๋Š”์ง€.
216
260
  - **Foundations/Brand & colour usage**: ์„ ํƒ๋œ ๊ณณ์„ ์ƒ‰์ด ์•„๋‹ˆ๋ผ ์ง„ํ•œ ๊ธ€์ž๋กœ ํ‘œ์‹œํ•˜๋Š”
@@ -88,6 +88,10 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
88
88
  bg: 'bg.subtle',
89
89
  color: 'fg.muted',
90
90
  borderColor: 'border.subtle',
91
+ // Nothing can be typed here, so hovering must not promise an edit.
92
+ _hover: {
93
+ borderColor: 'border.subtle',
94
+ },
91
95
  }}
92
96
  _disabled={{
93
97
  ...disabledControl,
@@ -144,6 +144,10 @@ What you still have to do:
144
144
  shows.
145
145
  - Never put the only copy of required information in a tooltip; touch screens
146
146
  have no hover.
147
+ - **Tooltip + Menu on one trigger: share `ids.trigger`.** Both make the
148
+ button their trigger and each gives it its own id, so the menu cannot find
149
+ its anchor and opens at the top-left of the page. Pass one `useId()` to
150
+ both: `<Menu ids={{ trigger: id }}><Tooltip ids={{ trigger: id }} โ€ฆ>`.
147
151
 
148
152
  ## Tokens
149
153
 
@@ -178,6 +182,10 @@ Tooltip์ด ์•Œ์•„์„œ ํ•˜๋Š” ์ผ
178
182
  ์ž์‹์€ ์‹ค์ œ ๋ฒ„ํŠผ, ๋งํฌ์ฒ˜๋Ÿผ ์ดˆ์ ์„ ๋ฐ›๋Š” ์š”์†Œ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋น„ํ™œ์„ฑ ๋ฒ„ํŠผ์€ ํฌ์ธํ„ฐ๋„ ์ดˆ์ ๋„
179
183
  ๋ฐ›์ง€ ์•Š์•„ ํˆดํŒ์ด ๋œจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
180
184
  - ๊ผญ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ํˆดํŒ์—๋งŒ ๋‘์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ„ฐ์น˜ ํ™”๋ฉด์—๋Š” ๋งˆ์šฐ์Šค ์˜ฌ๋ฆฌ๊ธฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
185
+ - **ํ•œ ๋ฒ„ํŠผ์— Tooltip๊ณผ Menu๋ฅผ ํ•จ๊ป˜ ๊ฑธ ๋•Œ๋Š” `ids.trigger`๋ฅผ ๊ฐ™์ด ์”๋‹ˆ๋‹ค.** ๋‘˜ ๋‹ค ๊ทธ ๋ฒ„ํŠผ์„
186
+ ์—ฌ๋Š” ์š”์†Œ๋กœ ์‚ผ๊ณ  ๊ฐ์ž ๋‹ค๋ฅธ id๋ฅผ ๋ถ™์ด๋ฏ€๋กœ, ๋ฉ”๋‰ด๊ฐ€ ๊ธฐ์ค€ ์œ„์น˜๋ฅผ ๋ชป ์ฐพ๊ณ  ํ™”๋ฉด ์™ผ์ชฝ ์œ„์—์„œ
187
+ ์—ด๋ฆฝ๋‹ˆ๋‹ค. `useId()` ํ•˜๋‚˜๋ฅผ ์–‘์ชฝ์— ๋„˜๊น๋‹ˆ๋‹ค:
188
+ `<Menu ids={{ trigger: id }}><Tooltip ids={{ trigger: id }} โ€ฆ>`.
181
189
 
182
190
  ## ํ† ํฐ
183
191
 
@@ -6,6 +6,7 @@ import { buttonVariantStyles } from '@/components/Button/Button.styles';
6
6
  import { FileInput } from '@/components/FileInput';
7
7
  import { LogicianProvider } from '@/components/LogicianProvider';
8
8
  import { baseMarkdownComponents } from '@/components/Markdown/Markdown';
9
+ import { NavList } from '@/components/NavList';
9
10
  import { Radio, RadioGroup } from '@/components/Radio';
10
11
  import { SelectField } from '@/components/Select';
11
12
  import {
@@ -292,6 +293,71 @@ describe('axe โ€” ๊ตฌ์กฐยทARIA ๊ทœ์น™', () => {
292
293
  </RadioGroup>,
293
294
  ],
294
295
  ['ํŒŒ์ผ ์ž…๋ ฅ', <FileInput key="file" onChange={() => {}} />],
296
+ [
297
+ // Rows are links or buttons with aria-current โ€” never tabs โ€” and the
298
+ // trailing action sits beside its row, not inside it (nested-interactive).
299
+ '๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ชฉ๋ก',
300
+ <NavList key="navlist" aria-label="์†Œ์Šค">
301
+ <NavList.Item href="/recent" isCurrent trailingVisual="3">
302
+ ์ตœ๊ทผ
303
+ </NavList.Item>
304
+ <NavList.Item href="/off" isDisabled>
305
+ ์‚ฌ์šฉ ๋ถˆ๊ฐ€
306
+ </NavList.Item>
307
+ <NavList.Group title="๊ณต์œ  ๋“œ๋ผ์ด๋ธŒ">
308
+ <NavList.Item
309
+ onClick={() => {}}
310
+ trailingAction={
311
+ <button type="button" aria-label="๋งˆ์ผ€ํŒ… ์˜ต์…˜">
312
+ โ€ฆ
313
+ </button>
314
+ }
315
+ >
316
+ ๋งˆ์ผ€ํŒ…
317
+ </NavList.Item>
318
+ <NavList.Item onClick={() => {}} isDisabled>
319
+ ๋ณด๊ด€๋จ
320
+ </NavList.Item>
321
+ </NavList.Group>
322
+ </NavList>,
323
+ ],
324
+ [
325
+ // Icon-only rows keep their label as the name; a new-window link says
326
+ // so in its name; a collapsible group is a disclosure with its list
327
+ // still labelled; a hover-revealed action is still in the tree.
328
+ '์ ‘ํžŒ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ ˆ์ผ๊ณผ ์ƒˆ ์ฐฝ ๋งํฌ',
329
+ <NavList key="navlist-collapsed" aria-label="๊ด€๋ฆฌ์ž ๋ฉ”๋‰ด" isCollapsed>
330
+ <NavList.Item href="/a" isCurrent leadingVisual={<svg />}>
331
+ ๋ฉค๋ฒ„ ๊ด€๋ฆฌ
332
+ </NavList.Item>
333
+ <NavList.Item
334
+ href="https://docs.example"
335
+ target="_blank"
336
+ leadingVisual={<svg />}
337
+ >
338
+ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ
339
+ </NavList.Item>
340
+ </NavList>,
341
+ ],
342
+ [
343
+ '์ ‘์„ ์ˆ˜ ์žˆ๋Š” ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฌถ์Œ',
344
+ <NavList key="navlist-collapsible" aria-label="์„ค์ •">
345
+ <NavList.Group title="๊ฒฐ์ œ" isCollapsible>
346
+ <NavList.Item
347
+ href="/b"
348
+ tooltip="์š”๊ธˆ์ œ์™€ ๊ฒฐ์ œ ์ˆ˜๋‹จ"
349
+ trailingActionVisibility="hover"
350
+ trailingAction={
351
+ <button type="button" aria-label="์š”๊ธˆ์ œ ์˜ต์…˜">
352
+ โ€ฆ
353
+ </button>
354
+ }
355
+ >
356
+ ์š”๊ธˆ์ œ
357
+ </NavList.Item>
358
+ </NavList.Group>
359
+ </NavList>,
360
+ ],
295
361
  ])('%s', async (_name, ui) => {
296
362
  const { container } = withProvider(ui);
297
363
  expect(await axeViolations(container)).toEqual([]);
@@ -348,7 +348,7 @@ When adding to `src/index.ts`, follow these categories:
348
348
 
349
349
  - **Core Components**: Button, Card, Badge, Tag
350
350
  - **Form Components**: Input, Select, Checkbox, Radio
351
- - **Navigation**: Breadcrumb, Pagination, Menu, Tabs
351
+ - **Navigation**: Breadcrumb, Pagination, Menu, Tabs, NavList
352
352
  - **Feedback**: Alert, Toast, Modal, Tooltip
353
353
  - **Data Display**: Table, Avatar, Typography, Code
354
354
  - **File Components**: FileInput, FileItem, FileList
package/src/index.ts CHANGED
@@ -66,6 +66,7 @@ export * from './components/MonthPicker';
66
66
  export * from './components/Popover';
67
67
 
68
68
  // Navigation Components
69
+ export * from './components/NavList';
69
70
  export * from './components/Pagination';
70
71
 
71
72
  // Input Variants
@@ -178,7 +179,12 @@ export {
178
179
  } from './theme/motion';
179
180
  export { cubicBezier } from './utils/cubicBezier';
180
181
  export { disabledControl, disabledText } from './utils/disabledControl';
181
- export { FOCUS_RING_SHADOW, focusRing } from './utils/focusRing';
182
+ export {
183
+ FOCUS_RING_INSET_SHADOW,
184
+ FOCUS_RING_SHADOW,
185
+ focusRing,
186
+ focusRingInset,
187
+ } from './utils/focusRing';
182
188
  export {
183
189
  staggerDelayMs,
184
190
  type StaggerProps,
@@ -25,3 +25,26 @@ export const focusRing = {
25
25
  boxShadow: FOCUS_RING_SHADOW,
26
26
  },
27
27
  } as const;
28
+
29
+ /**
30
+ * The same two bands as {@link FOCUS_RING_SHADOW}, drawn INSIDE the element's
31
+ * edge: the outer 2px in `focus.ring`, then the 2px `bg.canvas` band that keeps
32
+ * the ring legible against whatever fill the element has.
33
+ *
34
+ * {@link focusRing} needs 4px of clear space on every side, because both bands
35
+ * paint outside the box. Rows packed into a list cannot give it that โ€” the band
36
+ * lands on the neighbouring row, which paints its own background back over
37
+ * half of it โ€” and a list that scrolls (`overflow-x: auto`) clips it outright.
38
+ * Same colour and weight, inside the box instead.
39
+ */
40
+ export const FOCUS_RING_INSET_SHADOW =
41
+ 'inset 0 0 0 2px var(--chakra-colors-focus-ring), inset 0 0 0 4px var(--chakra-colors-bg-canvas)';
42
+
43
+ /** {@link focusRing}'s inset counterpart, for rows in a dense or scrolling list. */
44
+ export const focusRingInset = {
45
+ _focus: { outline: 'none' },
46
+ _focusVisible: {
47
+ outline: 'none',
48
+ boxShadow: FOCUS_RING_INSET_SHADOW,
49
+ },
50
+ } as const;