@surflane/ui 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +1 -1
  2. package/dist/app-shell.d.ts +44 -3
  3. package/dist/app-shell.js +81 -8
  4. package/dist/bottom-navigation.d.ts +66 -0
  5. package/dist/bottom-navigation.js +36 -0
  6. package/dist/chart.d.ts +16 -0
  7. package/dist/chart.js +103 -48
  8. package/dist/css/accordion.css +11 -0
  9. package/dist/css/alert-dialog.css +11 -0
  10. package/{src → dist/css}/all.css +1 -1
  11. package/dist/css/app-shell.css +12 -0
  12. package/dist/css/autocomplete.css +15 -0
  13. package/dist/css/avatar.css +10 -0
  14. package/dist/css/badge.css +10 -0
  15. package/dist/css/bottom-navigation.css +8 -0
  16. package/dist/css/breadcrumbs.css +12 -0
  17. package/dist/css/button.css +8 -0
  18. package/dist/css/card.css +11 -0
  19. package/dist/css/chart.css +13 -0
  20. package/dist/css/checkbox-group.css +9 -0
  21. package/dist/css/checkbox.css +9 -0
  22. package/dist/css/choice-label.css +9 -0
  23. package/dist/css/code.css +13 -0
  24. package/dist/css/collapsible.css +11 -0
  25. package/dist/css/combobox.css +14 -0
  26. package/dist/css/context-menu.css +15 -0
  27. package/dist/css/date-picker.css +12 -0
  28. package/dist/css/dialog.css +10 -0
  29. package/dist/css/drawer.css +11 -0
  30. package/dist/css/empty-state.css +9 -0
  31. package/dist/css/field.css +10 -0
  32. package/dist/css/fieldset.css +8 -0
  33. package/dist/css/file-upload.css +17 -0
  34. package/dist/css/filter-bar.css +8 -0
  35. package/dist/css/form.css +8 -0
  36. package/dist/css/icon.css +8 -0
  37. package/dist/css/inline-message.css +10 -0
  38. package/dist/css/input.css +9 -0
  39. package/dist/css/layout.css +9 -0
  40. package/dist/css/link.css +9 -0
  41. package/dist/css/list.css +9 -0
  42. package/dist/css/markdown.css +18 -0
  43. package/dist/css/menu.css +14 -0
  44. package/dist/css/menubar.css +9 -0
  45. package/dist/css/meter.css +9 -0
  46. package/dist/css/native-select.css +13 -0
  47. package/dist/css/navigation-menu.css +11 -0
  48. package/dist/css/number-field.css +11 -0
  49. package/dist/css/otp-field.css +10 -0
  50. package/dist/css/page-header.css +11 -0
  51. package/dist/css/pagination.css +11 -0
  52. package/dist/css/popover.css +9 -0
  53. package/dist/css/preview-card.css +11 -0
  54. package/dist/css/progress.css +9 -0
  55. package/dist/css/radio-group.css +9 -0
  56. package/dist/css/rules/accordion.css +21 -0
  57. package/dist/css/rules/alert-dialog.css +23 -0
  58. package/dist/css/rules/app-shell.css +227 -0
  59. package/dist/css/rules/autocomplete.css +3 -0
  60. package/dist/css/rules/avatar.css +17 -0
  61. package/dist/css/rules/badge.css +17 -0
  62. package/dist/css/rules/bar.css +38 -0
  63. package/dist/css/rules/base.css +46 -0
  64. package/dist/css/rules/bottom-navigation.css +142 -0
  65. package/dist/css/rules/breadcrumbs.css +9 -0
  66. package/dist/css/rules/button.css +131 -0
  67. package/dist/css/rules/card.css +10 -0
  68. package/dist/css/rules/chart.css +264 -0
  69. package/dist/css/rules/checkbox-group.css +3 -0
  70. package/dist/css/rules/checkbox.css +25 -0
  71. package/dist/css/rules/choice-label.css +52 -0
  72. package/dist/css/rules/choice.css +104 -0
  73. package/dist/css/rules/code.css +9 -0
  74. package/dist/css/rules/collapsible.css +12 -0
  75. package/dist/css/rules/combobox.css +177 -0
  76. package/dist/css/rules/context-menu.css +9 -0
  77. package/dist/css/rules/date-picker.css +301 -0
  78. package/dist/css/rules/dialog.css +160 -0
  79. package/dist/css/rules/disclosure.css +107 -0
  80. package/dist/css/rules/drawer.css +18 -0
  81. package/dist/css/rules/empty-state.css +9 -0
  82. package/dist/css/rules/field-control.css +70 -0
  83. package/dist/css/rules/field.css +56 -0
  84. package/dist/css/rules/fieldset.css +23 -0
  85. package/dist/css/rules/file-upload.css +156 -0
  86. package/dist/css/rules/filter-bar.css +56 -0
  87. package/dist/css/rules/form.css +15 -0
  88. package/dist/css/rules/gauge.css +61 -0
  89. package/dist/css/rules/icon.css +27 -0
  90. package/dist/css/rules/inline-message.css +10 -0
  91. package/dist/css/rules/input-group.css +69 -0
  92. package/dist/css/rules/input.css +33 -0
  93. package/dist/css/rules/item.css +14 -0
  94. package/dist/css/rules/layout.css +3 -0
  95. package/dist/css/rules/link.css +9 -0
  96. package/dist/css/rules/list.css +10 -0
  97. package/dist/css/rules/markdown.css +63 -0
  98. package/dist/css/rules/menu.css +97 -0
  99. package/dist/css/rules/menubar.css +18 -0
  100. package/dist/css/rules/meter.css +3 -0
  101. package/dist/css/rules/native-select.css +3 -0
  102. package/dist/css/rules/navigation-menu.css +159 -0
  103. package/dist/css/rules/number-field.css +55 -0
  104. package/dist/css/rules/otp-field.css +68 -0
  105. package/dist/css/rules/page-header.css +3 -0
  106. package/dist/css/rules/pagination.css +3 -0
  107. package/dist/css/rules/panel.css +221 -0
  108. package/dist/css/rules/popover.css +41 -0
  109. package/dist/css/rules/popup.css +27 -0
  110. package/dist/css/rules/preview-card.css +60 -0
  111. package/dist/css/rules/progress.css +14 -0
  112. package/dist/css/rules/radio-group.css +26 -0
  113. package/dist/css/rules/scroll-area.css +101 -0
  114. package/dist/css/rules/select.css +88 -0
  115. package/dist/css/rules/separator.css +3 -0
  116. package/dist/css/rules/sheet.css +3 -0
  117. package/dist/css/rules/sizing.css +140 -0
  118. package/dist/css/rules/skeleton.css +41 -0
  119. package/dist/css/rules/slider.css +123 -0
  120. package/dist/css/rules/sparkline.css +52 -0
  121. package/dist/css/rules/spinner.css +38 -0
  122. package/dist/css/rules/stat.css +59 -0
  123. package/dist/css/rules/switch.css +50 -0
  124. package/dist/css/rules/syntax-highlighter.css +82 -0
  125. package/dist/css/rules/table.css +28 -0
  126. package/dist/css/rules/tabs.css +126 -0
  127. package/dist/css/rules/textarea.css +21 -0
  128. package/dist/css/rules/toast.css +247 -0
  129. package/dist/css/rules/toggle-group.css +3 -0
  130. package/dist/css/rules/toggle.css +27 -0
  131. package/dist/css/rules/toolbar.css +62 -0
  132. package/dist/css/rules/tooltip.css +38 -0
  133. package/dist/css/rules/tree.css +98 -0
  134. package/dist/css/rules/typography.css +3 -0
  135. package/dist/css/scroll-area.css +8 -0
  136. package/dist/css/select.css +12 -0
  137. package/dist/css/separator.css +9 -0
  138. package/dist/css/sheet.css +11 -0
  139. package/dist/css/sizing.css +7 -0
  140. package/dist/css/skeleton.css +8 -0
  141. package/dist/css/slider.css +8 -0
  142. package/dist/css/sparkline.css +8 -0
  143. package/dist/css/spinner.css +8 -0
  144. package/dist/css/stat.css +9 -0
  145. package/dist/css/styles.css +100 -0
  146. package/dist/css/switch.css +9 -0
  147. package/dist/css/syntax-highlighter.css +9 -0
  148. package/dist/css/table.css +12 -0
  149. package/dist/css/tabs.css +8 -0
  150. package/dist/css/textarea.css +10 -0
  151. package/dist/css/toast.css +11 -0
  152. package/dist/css/toggle-group.css +9 -0
  153. package/dist/css/toggle.css +10 -0
  154. package/dist/css/toolbar.css +12 -0
  155. package/dist/css/tooltip.css +9 -0
  156. package/dist/css/tree.css +10 -0
  157. package/dist/css/typography.css +9 -0
  158. package/dist/format.d.ts +25 -0
  159. package/dist/format.js +42 -0
  160. package/dist/icon.d.ts +1 -0
  161. package/dist/icon.js +1 -0
  162. package/dist/index.d.ts +7 -3
  163. package/dist/index.js +6 -2
  164. package/dist/sparkline.d.ts +41 -0
  165. package/dist/sparkline.js +50 -0
  166. package/dist/stat.d.ts +59 -0
  167. package/dist/stat.js +26 -0
  168. package/dist/status.d.ts +1 -1
  169. package/dist/status.js +1 -0
  170. package/dist/toast.d.ts +57 -1
  171. package/dist/toast.js +73 -2
  172. package/dist/tooltip.d.ts +9 -3
  173. package/dist/tooltip.js +4 -4
  174. package/package.json +90 -7
  175. package/src/styles.css +0 -3948
@@ -0,0 +1,227 @@
1
+ /* App shell / Sidebar, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.components {
6
+ /* Application frame slots: navigation state and product routing remain caller-owned.
7
+ The shell is never rounded: its edge is the window's, or the edge of a container
8
+ that frames it and clips it. Only the page surface inside it is rounded. */
9
+ .pui-app-shell {
10
+ --app-shell-inset: var(--space-3);
11
+ /* --app-shell-radius, if a caller still sets it, keeps deriving the page radius. */
12
+ --app-shell-page-radius: max(
13
+ 0px,
14
+ calc(
15
+ var(--app-shell-radius, var(--radius-sheet)) - var(--app-shell-inset)
16
+ )
17
+ );
18
+ --app-shell-sidebar-width: 216px;
19
+ /* The collapsed sidebar's width. Set it on the shell for a narrower rail. */
20
+ --app-shell-rail-width: 64px;
21
+ display: grid;
22
+ grid-template-columns: var(--app-shell-sidebar-width) minmax(0, 1fr);
23
+ grid-template-rows: auto minmax(0, 1fr);
24
+ gap: var(--space-4);
25
+ min-inline-size: 0;
26
+ padding: var(--app-shell-inset);
27
+ background: var(--chrome);
28
+ }
29
+ .pui-app-shell[data-sidebar='collapsed'] {
30
+ --app-shell-sidebar-width: var(--app-shell-rail-width);
31
+ }
32
+ .pui-app-shell-header {
33
+ grid-column: 1 / -1;
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: space-between;
37
+ gap: var(--space-3);
38
+ min-inline-size: 0;
39
+ }
40
+ .pui-app-shell-sidebar {
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: var(--space-3);
44
+ min-inline-size: 0;
45
+ min-block-size: 0;
46
+ padding-inline: var(--space-3);
47
+ }
48
+ .pui-app-shell-content {
49
+ min-inline-size: 0;
50
+ min-block-size: 0;
51
+ padding: var(--space-6);
52
+ background: var(--canvas);
53
+ border-radius: var(--app-shell-page-radius);
54
+ overflow-wrap: anywhere;
55
+ }
56
+ .pui-app-shell-footer {
57
+ margin-block-start: auto;
58
+ flex: none;
59
+ }
60
+ .pui-app-shell-link {
61
+ display: grid;
62
+ grid-template-columns: 40px minmax(0, 1fr);
63
+ align-items: center;
64
+ min-block-size: 40px;
65
+ border-radius: var(--radius-md);
66
+ text-decoration: none;
67
+ color: var(--ink-body);
68
+ transition: background-color var(--duration-fast) var(--ease-out);
69
+ }
70
+ .pui-app-shell-link:hover {
71
+ background: var(--interaction-hover-bg);
72
+ }
73
+ .pui-app-shell-link:active:not([aria-current='page']) {
74
+ background: var(--interaction-active-bg);
75
+ }
76
+ .pui-app-shell-link[aria-current='page'] {
77
+ background: var(--selected);
78
+ color: var(--ink);
79
+ font-weight: var(--weight-medium);
80
+ }
81
+ .pui-app-shell-link:focus-visible {
82
+ outline: var(--focus-ring-width) solid var(--interaction-focus);
83
+ outline-offset: var(--focus-ring-inset);
84
+ }
85
+ .pui-app-shell-icon {
86
+ display: grid;
87
+ place-items: center;
88
+ inline-size: 40px;
89
+ }
90
+ .pui-app-shell-label {
91
+ min-inline-size: 0;
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ white-space: nowrap;
95
+ padding-inline-end: var(--space-2);
96
+ }
97
+ .pui-app-shell[data-sidebar='collapsed'] .pui-app-shell-label {
98
+ position: absolute;
99
+ inline-size: 1px;
100
+ block-size: 1px;
101
+ padding: 0;
102
+ clip-path: inset(50%);
103
+ overflow: hidden;
104
+ }
105
+ /* On a phone the frame costs more room than it gives: the page meets the screen's
106
+ edges, and only the header keeps its inset. */
107
+ @media (max-width: 760px) {
108
+ .pui-app-shell {
109
+ grid-template-columns: minmax(0, 1fr);
110
+ grid-template-rows: auto minmax(0, 1fr);
111
+ gap: 0;
112
+ padding: 0;
113
+ }
114
+ .pui-app-shell > .pui-app-shell-header {
115
+ padding: var(--app-shell-inset);
116
+ }
117
+ .pui-app-shell-sidebar {
118
+ display: none;
119
+ }
120
+ .pui-app-shell-content {
121
+ padding: var(--space-4);
122
+ border-radius: 0;
123
+ }
124
+ }
125
+ @media (max-width: 760px), (pointer: coarse) {
126
+ .pui-app-shell-link {
127
+ min-block-size: var(--control-target-size);
128
+ }
129
+ }
130
+ @media (prefers-reduced-motion: reduce) {
131
+ .pui-app-shell-link {
132
+ transition: none;
133
+ }
134
+ }
135
+ @media (forced-colors: active) {
136
+ .pui-app-shell-link[aria-current='page'] {
137
+ outline: 1px solid Highlight;
138
+ }
139
+ }
140
+ /* Workspace variant: product sidebar owns the brand; the content scrollport is caller-provided. */
141
+ .pui-app-shell[data-layout='workspace'] {
142
+ --app-shell-sidebar-width: 224px;
143
+ --app-shell-rail-width: 72px;
144
+ grid-template-rows: minmax(0, 1fr);
145
+ gap: 0;
146
+ padding: 0;
147
+ transition: grid-template-columns var(--duration-base) var(--ease-out);
148
+ }
149
+ .pui-app-shell[data-layout='workspace'][data-sidebar='collapsed'] {
150
+ --app-shell-sidebar-width: var(--app-shell-rail-width);
151
+ }
152
+ .pui-app-shell[data-layout='workspace'] > .pui-app-shell-sidebar {
153
+ position: relative;
154
+ grid-row: 1;
155
+ grid-column: 1;
156
+ gap: 0;
157
+ padding: 0;
158
+ z-index: 1;
159
+ }
160
+ .pui-app-shell[data-layout='workspace'] > .pui-app-shell-content {
161
+ grid-row: 1;
162
+ grid-column: 2;
163
+ margin: 0;
164
+ padding: 0;
165
+ background: transparent;
166
+ border-radius: 0;
167
+ }
168
+ /* The page surface: concentric with the shell, inset on every side but the
169
+ * sidebar's. Stretch or size it from its container: a percentage height here
170
+ * would resolve against a scroll content that sizes from the page itself. */
171
+ .pui-app-shell-page {
172
+ box-sizing: border-box;
173
+ min-inline-size: 0;
174
+ margin: var(--app-shell-inset);
175
+ margin-inline-start: 0;
176
+ padding: var(--space-6);
177
+ background: var(--canvas);
178
+ border-radius: var(--app-shell-page-radius);
179
+ }
180
+ /* On a phone the page meets the screen's edges, below the mobile header. */
181
+ @media (max-width: 760px) {
182
+ .pui-app-shell-page {
183
+ margin: 0;
184
+ padding: var(--space-4);
185
+ border-radius: 0;
186
+ }
187
+ }
188
+ .pui-app-shell[data-layout='workspace'] .pui-app-shell-footer {
189
+ padding: var(--space-4);
190
+ }
191
+ .pui-app-shell[data-layout='workspace'] .pui-app-shell-label {
192
+ transition:
193
+ opacity 80ms var(--ease-out) 160ms,
194
+ visibility 0s 160ms;
195
+ }
196
+ .pui-app-shell[data-layout='workspace'][data-sidebar='collapsed']
197
+ .pui-app-shell-label {
198
+ opacity: 0;
199
+ visibility: hidden;
200
+ transition: none;
201
+ }
202
+ @media (min-width: 761px) {
203
+ .pui-app-shell[data-layout='workspace'] > .pui-app-shell-header {
204
+ display: none;
205
+ }
206
+ }
207
+ @media (max-width: 760px) {
208
+ .pui-app-shell[data-layout='workspace'] {
209
+ grid-template-columns: minmax(0, 1fr);
210
+ grid-template-rows: auto minmax(0, 1fr);
211
+ }
212
+ .pui-app-shell[data-layout='workspace'] > .pui-app-shell-header {
213
+ padding: var(--space-3);
214
+ }
215
+ .pui-app-shell[data-layout='workspace'] > .pui-app-shell-content {
216
+ grid-column: 1;
217
+ grid-row: 2;
218
+ margin: 0;
219
+ }
220
+ }
221
+ @media (prefers-reduced-motion: reduce) {
222
+ .pui-app-shell[data-layout='workspace'],
223
+ .pui-app-shell[data-layout='workspace'] .pui-app-shell-label {
224
+ transition: none;
225
+ }
226
+ }
227
+ }
@@ -0,0 +1,3 @@
1
+ /* Autocomplete, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
@@ -0,0 +1,17 @@
1
+ /* Avatar, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.sizes {
6
+ :where([data-ui-size], [data-ui-size] *).pui-avatar {
7
+ width: var(--ui-avatar);
8
+ height: var(--ui-avatar);
9
+ }
10
+ :where([data-ui-size], [data-ui-size] *).pui-avatar[data-ui-size] {
11
+ font-size: clamp(
12
+ var(--type-caption-size),
13
+ calc(var(--ui-avatar) / 3),
14
+ var(--size-font-large)
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ /* Badge / Status, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.sizes {
6
+ :where([data-ui-size], [data-ui-size] *).pui-badge {
7
+ padding: var(--ui-badge-padding-y) var(--ui-badge-padding-x);
8
+ font-size: var(--ui-badge-font);
9
+ }
10
+ @media (forced-colors: active) {
11
+ :where([data-ui-size], [data-ui-size] *).pui-badge {
12
+ border: 1px solid CanvasText;
13
+ padding: calc(var(--ui-badge-padding-y) - 1px)
14
+ calc(var(--ui-badge-padding-x) - 1px);
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,38 @@
1
+ /* Shared by ToggleGroup, Toolbar and Menubar, and the pressed state of their items. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.components {
6
+ .pui-toggle-group,
7
+ .pui-toolbar,
8
+ .pui-menubar {
9
+ display: flex;
10
+ flex-wrap: wrap;
11
+ align-items: center;
12
+ gap: var(--space-1);
13
+ min-width: 0;
14
+ }
15
+ .pui-toggle-group[data-orientation='vertical'],
16
+ .pui-toolbar[data-orientation='vertical'],
17
+ .pui-menubar[data-orientation='vertical'] {
18
+ flex-direction: column;
19
+ align-items: flex-start;
20
+ width: fit-content;
21
+ max-width: 100%;
22
+ }
23
+ .pui-toggle,
24
+ .pui-toolbar-button {
25
+ position: relative;
26
+ }
27
+ @media (forced-colors: active) {
28
+ .pui-toggle[data-pressed]::after,
29
+ .pui-toolbar-button[aria-pressed='true']::after {
30
+ content: '';
31
+ position: absolute;
32
+ inset: 3px;
33
+ border: 1px solid CanvasText;
34
+ border-radius: inherit;
35
+ pointer-events: none;
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,46 @@
1
+ /* Shared by every component stylesheet: the tokens, and rules with no specificity. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.components {
6
+ @keyframes pui-progress-pulse {
7
+ 50% {
8
+ opacity: 0.45;
9
+ }
10
+ }
11
+ /* Touch feedback is each control's own hover and pressed layer, drawn in its own
12
+ shape. The browser's tap highlight is a rectangle over the box, so it is off. */
13
+ :where(
14
+ .pui-button,
15
+ .pui-tabs-tab,
16
+ .pui-disclosure-trigger,
17
+ .pui-app-shell-link,
18
+ .pui-tree-item,
19
+ .pui-tree-toggle,
20
+ .pui-chart-legend-button,
21
+ .pui-checkbox,
22
+ .pui-radio,
23
+ .pui-switch,
24
+ .pui-choice-label,
25
+ .pui-select-trigger,
26
+ .pui-select-item,
27
+ .pui-combobox-item,
28
+ .pui-combobox-action,
29
+ .pui-combobox-chip-remove,
30
+ .pui-menu-item,
31
+ .pui-navigation-trigger,
32
+ .pui-navigation-link,
33
+ .pui-link,
34
+ .pui-breadcrumb-item a,
35
+ .pui-calendar-day,
36
+ .pui-calendar-nav,
37
+ .pui-date-trigger,
38
+ .pui-file-trigger,
39
+ .pui-number-step,
40
+ .pui-slider-control,
41
+ .pui-toast-action,
42
+ .pui-toast-close
43
+ ) {
44
+ -webkit-tap-highlight-color: transparent;
45
+ }
46
+ }
@@ -0,0 +1,142 @@
1
+ /* Bottom navigation, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.components {
6
+ /* The phone's chrome along the bottom edge, as the sidebar is on wider screens. It
7
+ shows where AppShell hides the sidebar, and pads the home indicator's safe area. */
8
+ .pui-bottom-nav {
9
+ box-sizing: border-box;
10
+ display: none;
11
+ inline-size: 100%;
12
+ padding-block-end: env(safe-area-inset-bottom, 0px);
13
+ border-block-start: 1px solid var(--line);
14
+ background: var(--chrome);
15
+ }
16
+ .pui-bottom-nav[data-visible='always'] {
17
+ display: block;
18
+ }
19
+ @media (max-width: 760px) {
20
+ .pui-bottom-nav {
21
+ display: block;
22
+ }
23
+ }
24
+ .pui-bottom-nav[data-position='fixed'] {
25
+ position: fixed;
26
+ inset-inline: 0;
27
+ inset-block-end: 0;
28
+ z-index: var(--layer-chrome);
29
+ }
30
+ .pui-bottom-nav-list {
31
+ display: flex;
32
+ block-size: var(--size-bottom-navigation);
33
+ margin: 0;
34
+ padding: 0 var(--space-1);
35
+ list-style: none;
36
+ }
37
+ .pui-bottom-nav-entry {
38
+ display: flex;
39
+ flex: 1 1 0;
40
+ min-inline-size: 0;
41
+ }
42
+ .pui-bottom-nav-item {
43
+ display: flex;
44
+ flex: 1;
45
+ flex-direction: column;
46
+ align-items: center;
47
+ justify-content: center;
48
+ gap: var(--space-1);
49
+ min-inline-size: 0;
50
+ min-block-size: var(--control-target-size);
51
+ color: var(--ink-muted);
52
+ font: var(--weight-medium)
53
+ var(--type-caption-size)/var(--type-caption-leading) var(--font-body);
54
+ letter-spacing: var(--type-caption-tracking);
55
+ text-decoration: none;
56
+ -webkit-tap-highlight-color: transparent;
57
+ }
58
+ /* The icon sits in a pill: the current destination fills it, touch presses it. */
59
+ .pui-bottom-nav-icon {
60
+ position: relative;
61
+ display: inline-flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ padding: var(--space-1) var(--space-4);
65
+ border-radius: var(--radius-full);
66
+ transition: background-color var(--duration-fast) var(--ease-out);
67
+ }
68
+ /* Icons take the medium size here unless their own size prop sets one. */
69
+ .pui-bottom-nav-icon > .pui-icon:not([data-size]) {
70
+ width: var(--icon-size-md);
71
+ height: var(--icon-size-md);
72
+ }
73
+ .pui-bottom-nav-item[aria-current='page'] {
74
+ color: var(--ink);
75
+ }
76
+ .pui-bottom-nav-item[aria-current='page'] .pui-bottom-nav-icon {
77
+ background: var(--selected);
78
+ }
79
+ @media (hover: hover) {
80
+ .pui-bottom-nav-item:hover .pui-bottom-nav-icon {
81
+ background-image: linear-gradient(
82
+ var(--interaction-hover-bg),
83
+ var(--interaction-hover-bg)
84
+ );
85
+ }
86
+ }
87
+ .pui-bottom-nav-item:active .pui-bottom-nav-icon {
88
+ background-image: linear-gradient(
89
+ var(--interaction-active-bg),
90
+ var(--interaction-active-bg)
91
+ );
92
+ }
93
+ .pui-bottom-nav-item:focus-visible {
94
+ outline: var(--focus-ring-width) solid var(--interaction-focus);
95
+ outline-offset: var(--focus-ring-inset);
96
+ border-radius: var(--radius-md);
97
+ }
98
+ .pui-bottom-nav-label {
99
+ max-inline-size: 100%;
100
+ padding-inline: var(--space-1);
101
+ overflow: hidden;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ }
105
+ /* A count or a dot at the pill's top corner, in the danger color. */
106
+ .pui-bottom-nav-badge {
107
+ position: absolute;
108
+ inset-block-start: calc(-1 * var(--space-1));
109
+ inset-inline-start: calc(50% + var(--space-1));
110
+ box-sizing: border-box;
111
+ min-inline-size: calc(var(--type-caption-leading) * 1em);
112
+ padding-inline: var(--space-1);
113
+ border-radius: var(--radius-full);
114
+ background: var(--status-danger-fg);
115
+ color: var(--canvas);
116
+ font-variant-numeric: tabular-nums;
117
+ text-align: center;
118
+ }
119
+ .pui-bottom-nav-badge[data-dot] {
120
+ inset-block-start: 0;
121
+ min-inline-size: 0;
122
+ inline-size: var(--space-2);
123
+ block-size: var(--space-2);
124
+ padding: 0;
125
+ }
126
+ @media (prefers-reduced-motion: reduce) {
127
+ .pui-bottom-nav-icon {
128
+ transition: none;
129
+ }
130
+ }
131
+ @media (forced-colors: active) {
132
+ .pui-bottom-nav {
133
+ border-block-start-color: CanvasText;
134
+ }
135
+ .pui-bottom-nav-item[aria-current='page'] .pui-bottom-nav-icon {
136
+ outline: 1px solid Highlight;
137
+ }
138
+ .pui-bottom-nav-badge {
139
+ border: 1px solid CanvasText;
140
+ }
141
+ }
142
+ }
@@ -0,0 +1,9 @@
1
+ /* Breadcrumbs, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.sizes {
6
+ :where([data-ui-size], [data-ui-size] *):is(.pui-breadcrumbs) {
7
+ font-size: var(--ui-font);
8
+ }
9
+ }
@@ -0,0 +1,131 @@
1
+ /* Button, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.components {
6
+ .pui-button {
7
+ box-sizing: border-box;
8
+ height: var(--control-height);
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: var(--space-2);
13
+ min-height: var(--control-height);
14
+ padding: 0 var(--size-button-padding-default);
15
+ border: 1px solid transparent;
16
+ border-radius: var(--radius-full);
17
+ font: var(--weight-medium) var(--type-label-size)/var(--type-label-leading)
18
+ var(--font-body);
19
+ letter-spacing: var(--type-label-tracking);
20
+ color: var(--ink);
21
+ background: var(--canvas);
22
+ /* A link rendered as a button drops the user-agent underline. */
23
+ text-decoration: none;
24
+ cursor: pointer;
25
+ }
26
+ .pui-button:is([data-variant='default'], [data-variant='primary']) {
27
+ /* On the brand fill the layer is its ink: light ink barely moves bright cyan. */
28
+ --interaction-hover-bg: oklch(from var(--on-brand) l c h / 0.08);
29
+ --interaction-active-bg: oklch(from var(--on-brand) l c h / 0.14);
30
+ background: var(--brand);
31
+ border-color: transparent;
32
+ color: var(--on-brand);
33
+ }
34
+ .pui-button[data-variant='outline'] {
35
+ border-color: var(--line);
36
+ background: transparent;
37
+ }
38
+ .pui-button[data-variant='ghost'] {
39
+ border-color: transparent;
40
+ background: transparent;
41
+ }
42
+ /* Hover adds the state layer over the variant's own fill, so a primary, a
43
+ pressed toggle and a transparent toolbar button all step by the same amount. */
44
+ .pui-button:hover:not(:disabled, [data-disabled], [data-variant='link']) {
45
+ background-image: linear-gradient(
46
+ var(--interaction-hover-bg),
47
+ var(--interaction-hover-bg)
48
+ );
49
+ }
50
+ .pui-button[data-variant='link']:hover:not(:disabled, [data-disabled]) {
51
+ text-decoration-thickness: 2px;
52
+ }
53
+ .pui-button:focus-visible {
54
+ outline: var(--focus-ring-width) solid var(--interaction-focus);
55
+ outline-offset: var(--focus-ring-offset);
56
+ }
57
+ .pui-button:is(:disabled, [data-disabled], [aria-disabled='true']) {
58
+ opacity: var(--control-disabled-opacity);
59
+ cursor: not-allowed;
60
+ }
61
+ /* Touch targets grow on narrow screens and on touch, as --control-height does. */
62
+ @media (max-width: 760px), (pointer: coarse) {
63
+ .pui-button {
64
+ min-height: var(--control-target-size);
65
+ }
66
+ }
67
+ .pui-button[data-variant='secondary'] {
68
+ background: var(--selected);
69
+ color: var(--ink);
70
+ }
71
+ .pui-button[data-variant='destructive'] {
72
+ background: var(--status-danger-bg);
73
+ color: var(--status-danger-fg);
74
+ border-color: transparent;
75
+ }
76
+ .pui-button[data-variant='link'] {
77
+ background: transparent;
78
+ color: var(--brand-ink);
79
+ border-color: transparent;
80
+ text-decoration: underline;
81
+ }
82
+ /* Active feedback is transient; persistent selection belongs to Toggle. */
83
+ .pui-button:active:not(:disabled, [data-disabled], [data-variant='link']) {
84
+ background-image: linear-gradient(
85
+ var(--interaction-active-bg),
86
+ var(--interaction-active-bg)
87
+ );
88
+ }
89
+ .pui-button[aria-busy='true'] {
90
+ cursor: progress;
91
+ }
92
+ @media (forced-colors: active) {
93
+ .pui-button {
94
+ border-color: ButtonText;
95
+ }
96
+ .pui-button:active {
97
+ background-image: none;
98
+ }
99
+ }
100
+ }
101
+ @layer surflane.sizes {
102
+ .pui-button[data-size='xs'] {
103
+ height: 24px;
104
+ min-height: 24px;
105
+ padding-inline: var(--space-2);
106
+ font-size: var(--type-caption-size);
107
+ letter-spacing: var(--type-caption-tracking);
108
+ gap: var(--space-1);
109
+ }
110
+ .pui-button[data-size^='icon'] {
111
+ width: var(--control-height);
112
+ padding: 0;
113
+ }
114
+ :where([data-ui-size], [data-ui-size] *).pui-button {
115
+ height: var(--control-height);
116
+ min-height: var(--control-height);
117
+ padding-inline: var(--ui-button-padding);
118
+ font-size: var(--ui-font);
119
+ line-height: var(--ui-leading);
120
+ }
121
+ :where([data-ui-size], [data-ui-size] *).pui-button[data-size^='icon'] {
122
+ inline-size: var(--control-height);
123
+ block-size: var(--control-height);
124
+ padding: 0;
125
+ border-radius: var(--radius-full);
126
+ }
127
+ :where([data-ui-size], [data-ui-size] *):is(.pui-button) > svg {
128
+ inline-size: var(--ui-icon);
129
+ block-size: var(--ui-icon);
130
+ }
131
+ }
@@ -0,0 +1,10 @@
1
+ /* Card / Surface, with the styles of what it renders. */
2
+ @layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
3
+ surflane.sizes;
4
+
5
+ @layer surflane.sizes {
6
+ :where([data-ui-size], [data-ui-size] *).pui-card {
7
+ padding: var(--surface-padding, var(--ui-card-padding));
8
+ gap: var(--ui-surface-padding);
9
+ }
10
+ }