@grantcodes/ui 2.3.0 → 2.4.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.
- package/CHANGELOG.md +13 -0
- package/package.json +2 -2
- package/src/css/all.css +7 -1
- package/src/css/base.css +240 -10
- package/src/css/layers.stories.js +113 -0
- package/src/lib/styles/all.css +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.4.0](https://github.com/grantcodes/ui/compare/ui-v2.3.0...ui-v2.4.0) (2026-03-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **ui:** add CSS layers and simplify component stylesheet bundle ([42c183c](https://github.com/grantcodes/ui/commit/42c183c303b81d8a4d0a7bb7a4d5f6482697b3d9))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **ui:** replace sanitize.css imports with inlined custom reset ([c97182b](https://github.com/grantcodes/ui/commit/c97182bc98406055f81e6b2433427e52723776b8))
|
|
14
|
+
* **ui:** replace sanitize.css with inlined custom reset ([f5fe733](https://github.com/grantcodes/ui/commit/f5fe733007783452a4f4612e846960e074386677))
|
|
15
|
+
|
|
3
16
|
## [2.3.0](https://github.com/grantcodes/ui/compare/ui-v2.2.0...ui-v2.3.0) (2026-03-25)
|
|
4
17
|
|
|
5
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grantcodes/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "A personal component system built with Lit web components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.js",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"require": "./src/css/themes/*"
|
|
29
29
|
},
|
|
30
30
|
"./styles/focus-ring.css": "./src/lib/styles/focus-ring.css",
|
|
31
|
+
"./styles/all.css": "./src/lib/styles/all.css",
|
|
31
32
|
"./components/*": {
|
|
32
33
|
"import": "./src/components/*",
|
|
33
34
|
"require": "./src/components/*"
|
|
@@ -45,7 +46,6 @@
|
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@lit/react": "^1.0.8",
|
|
47
48
|
"lit": "^3.3.1",
|
|
48
|
-
"sanitize.css": "^13.0.0",
|
|
49
49
|
"shiki": "^3.17.1",
|
|
50
50
|
"@grantcodes/style-dictionary": "^1.3.1"
|
|
51
51
|
},
|
package/src/css/all.css
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
@
|
|
1
|
+
@layer reset, base, utilities, components;
|
|
2
|
+
|
|
3
|
+
@import "./base.css" layer(reset);
|
|
4
|
+
@import "./typography.css" layer(base);
|
|
5
|
+
@import "./elements.css" layer(base);
|
|
6
|
+
@import "./util/index.css" layer(utilities);
|
|
7
|
+
@import "./helpers.css" layer(utilities);
|
package/src/css/base.css
CHANGED
|
@@ -1,13 +1,243 @@
|
|
|
1
|
-
/* CSS
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
/* CSS reset — inspired by sanitize.css (https://csstools.github.io/sanitize.css/)
|
|
2
|
+
Inlined to avoid broken relative node_modules paths when installed externally. */
|
|
3
|
+
|
|
4
|
+
/* Box sizing and background */
|
|
5
|
+
*,
|
|
6
|
+
::before,
|
|
7
|
+
::after {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
background-repeat: no-repeat;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
::before,
|
|
13
|
+
::after {
|
|
14
|
+
text-decoration: inherit;
|
|
15
|
+
vertical-align: inherit;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Root defaults */
|
|
19
|
+
:where(:root) {
|
|
20
|
+
cursor: default;
|
|
21
|
+
line-height: 1.5;
|
|
22
|
+
overflow-wrap: break-word;
|
|
23
|
+
-moz-tab-size: 4;
|
|
24
|
+
tab-size: 4;
|
|
25
|
+
-webkit-tap-highlight-color: transparent;
|
|
26
|
+
-webkit-text-size-adjust: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Typography — system font stack */
|
|
30
|
+
html {
|
|
31
|
+
font-family:
|
|
32
|
+
system-ui,
|
|
33
|
+
-apple-system,
|
|
34
|
+
"Segoe UI",
|
|
35
|
+
"Roboto",
|
|
36
|
+
"Ubuntu",
|
|
37
|
+
"Cantarell",
|
|
38
|
+
"Noto Sans",
|
|
39
|
+
sans-serif,
|
|
40
|
+
"Apple Color Emoji",
|
|
41
|
+
"Segoe UI Emoji",
|
|
42
|
+
"Segoe UI Symbol",
|
|
43
|
+
"Noto Color Emoji";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
code,
|
|
47
|
+
kbd,
|
|
48
|
+
samp,
|
|
49
|
+
pre {
|
|
50
|
+
font-family:
|
|
51
|
+
ui-monospace,
|
|
52
|
+
"Menlo",
|
|
53
|
+
"Consolas",
|
|
54
|
+
"Roboto Mono",
|
|
55
|
+
"Ubuntu Monospace",
|
|
56
|
+
"Noto Mono",
|
|
57
|
+
"Oxygen Mono",
|
|
58
|
+
"Liberation Mono",
|
|
59
|
+
monospace,
|
|
60
|
+
"Apple Color Emoji",
|
|
61
|
+
"Segoe UI Emoji",
|
|
62
|
+
"Segoe UI Symbol",
|
|
63
|
+
"Noto Color Emoji";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Sections */
|
|
67
|
+
:where(body) {
|
|
68
|
+
margin: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:where(h1) {
|
|
72
|
+
font-size: 2em;
|
|
73
|
+
margin: 0.67em 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Grouping */
|
|
77
|
+
:where(dl, ol, ul) :where(dl, ol, ul) {
|
|
78
|
+
margin: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:where(hr) {
|
|
82
|
+
color: inherit;
|
|
83
|
+
height: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:where(nav) :where(ol, ul) {
|
|
87
|
+
list-style-type: none;
|
|
88
|
+
padding: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Prevent VoiceOver from ignoring list semantics in Safari */
|
|
92
|
+
:where(nav li)::before {
|
|
93
|
+
content: "\200B";
|
|
94
|
+
float: left;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:where(pre) {
|
|
98
|
+
font-size: 1em;
|
|
99
|
+
overflow: auto;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Text-level semantics */
|
|
103
|
+
:where(abbr[title]) {
|
|
104
|
+
text-decoration: underline dotted;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:where(b, strong) {
|
|
108
|
+
font-weight: bolder;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:where(code, kbd, samp) {
|
|
112
|
+
font-size: 1em;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
:where(small) {
|
|
116
|
+
font-size: 80%;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Embedded content */
|
|
120
|
+
:where(audio, canvas, iframe, img, svg, video) {
|
|
121
|
+
vertical-align: middle;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
:where(iframe) {
|
|
125
|
+
border-style: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
:where(svg:not([fill])) {
|
|
129
|
+
fill: currentColor;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Tabular data */
|
|
133
|
+
:where(table) {
|
|
134
|
+
border-collapse: collapse;
|
|
135
|
+
border-color: inherit;
|
|
136
|
+
text-indent: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Forms — baseline normalisation */
|
|
140
|
+
:where(button, input, select) {
|
|
141
|
+
margin: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
:where(button, [type="button" i], [type="reset" i], [type="submit" i]) {
|
|
145
|
+
-webkit-appearance: button;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
:where(fieldset) {
|
|
149
|
+
border: 1px solid #a0a0a0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
:where(progress) {
|
|
153
|
+
vertical-align: baseline;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
:where(textarea) {
|
|
157
|
+
margin: 0;
|
|
158
|
+
resize: vertical;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
:where([type="search" i]) {
|
|
162
|
+
-webkit-appearance: textfield;
|
|
163
|
+
outline-offset: -2px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
::-webkit-inner-spin-button,
|
|
167
|
+
::-webkit-outer-spin-button {
|
|
168
|
+
height: auto;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
::-webkit-input-placeholder {
|
|
172
|
+
color: inherit;
|
|
173
|
+
opacity: 0.54;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
::-webkit-search-decoration {
|
|
177
|
+
-webkit-appearance: none;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
::-webkit-file-upload-button {
|
|
181
|
+
-webkit-appearance: button;
|
|
182
|
+
font: inherit;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Forms — typography and colour inheritance */
|
|
186
|
+
:where(button, input, select, textarea) {
|
|
187
|
+
background-color: transparent;
|
|
188
|
+
border: 1px solid WindowFrame;
|
|
189
|
+
color: inherit;
|
|
190
|
+
font: inherit;
|
|
191
|
+
letter-spacing: inherit;
|
|
192
|
+
padding: 0.25em 0.375em;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
:where([type="color" i], [type="range" i]) {
|
|
196
|
+
border-width: 0;
|
|
197
|
+
padding: 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Interactive */
|
|
201
|
+
:where(details > summary:first-of-type) {
|
|
202
|
+
display: list-item;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Accessibility */
|
|
206
|
+
:where([aria-busy="true" i]) {
|
|
207
|
+
cursor: progress;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
:where([aria-controls]) {
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
:where([aria-disabled="true" i], [disabled]) {
|
|
215
|
+
cursor: not-allowed;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
:where([aria-hidden="false" i][hidden]) {
|
|
219
|
+
display: initial;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
:where([aria-hidden="false" i][hidden]:not(:focus)) {
|
|
223
|
+
clip: rect(0, 0, 0, 0);
|
|
224
|
+
position: absolute;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Reduced motion — !important is required here to override any animation/transition regardless of specificity */
|
|
228
|
+
@media (prefers-reduced-motion: reduce) {
|
|
229
|
+
*,
|
|
230
|
+
::before,
|
|
231
|
+
::after {
|
|
232
|
+
animation-delay: -1ms !important;
|
|
233
|
+
animation-duration: 1ms !important;
|
|
234
|
+
animation-iteration-count: 1 !important;
|
|
235
|
+
background-attachment: initial !important;
|
|
236
|
+
scroll-behavior: auto !important;
|
|
237
|
+
transition-delay: 0s !important;
|
|
238
|
+
transition-duration: 0s !important;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
11
241
|
|
|
12
242
|
/* Design tokens are provided by @grantcodes/style-dictionary and applied to :root */
|
|
13
243
|
:root {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
|
|
3
|
+
const meta = {
|
|
4
|
+
title: "Styles/CSS Layers",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default meta;
|
|
8
|
+
|
|
9
|
+
export const LayerOrder = {
|
|
10
|
+
render: () => html`
|
|
11
|
+
<div style="max-width: 40rem;">
|
|
12
|
+
<h2>CSS Layers</h2>
|
|
13
|
+
<p>Styles are organized into CSS cascade layers, from lowest to highest priority:</p>
|
|
14
|
+
|
|
15
|
+
<ol>
|
|
16
|
+
<li><strong>reset</strong> — sanitize.css, root defaults</li>
|
|
17
|
+
<li><strong>base</strong> — typography, element styles</li>
|
|
18
|
+
<li><strong>utilities</strong> — focus-ring, helpers</li>
|
|
19
|
+
<li><strong>components</strong> — web component styles</li>
|
|
20
|
+
</ol>
|
|
21
|
+
|
|
22
|
+
<p>Unlayered styles (your overrides, theme variables) always win.</p>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const LayerOverrideDemo = {
|
|
28
|
+
render: () => html`
|
|
29
|
+
<style>
|
|
30
|
+
/* This override is unlayered, so it beats all layers */
|
|
31
|
+
.layer-demo-override {
|
|
32
|
+
color: var(--g-theme-color-content-brand, rebeccapurple);
|
|
33
|
+
font-weight: bold;
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
36
|
+
<div style="max-width: 40rem; display: flex; flex-direction: column; gap: 1.5rem;">
|
|
37
|
+
<h2>Layer Override Demo</h2>
|
|
38
|
+
<p>Because styles are layered, overriding component/base styles is straightforward — no <code>!important</code> needed.</p>
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<h3>Default paragraph (base layer)</h3>
|
|
42
|
+
<p>This paragraph uses the default typography styles from the <code>base</code> layer.</p>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div>
|
|
46
|
+
<h3>Overridden paragraph (unlayered)</h3>
|
|
47
|
+
<p class="layer-demo-override">This paragraph is overridden with an unlayered style — it wins over the base layer automatically.</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div>
|
|
51
|
+
<h3>Component with override</h3>
|
|
52
|
+
<grantcodes-button>Default button</grantcodes-button>
|
|
53
|
+
<style>
|
|
54
|
+
.custom-btn::part(button) {
|
|
55
|
+
border-radius: 999px;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
58
|
+
<grantcodes-button class="custom-btn">Custom border radius (unlayered override)</grantcodes-button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const AllLayers = {
|
|
65
|
+
render: () => html`
|
|
66
|
+
<style>
|
|
67
|
+
.layers-grid {
|
|
68
|
+
display: grid;
|
|
69
|
+
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
|
70
|
+
gap: 1rem;
|
|
71
|
+
max-width: 60rem;
|
|
72
|
+
}
|
|
73
|
+
.layer-card {
|
|
74
|
+
padding: 1rem;
|
|
75
|
+
border: 1px solid var(--g-theme-color-border-default, #ccc);
|
|
76
|
+
border-radius: var(--g-theme-border-radius-md, 0.5rem);
|
|
77
|
+
}
|
|
78
|
+
.layer-card h4 {
|
|
79
|
+
margin-top: 0;
|
|
80
|
+
}
|
|
81
|
+
.layer-card code {
|
|
82
|
+
font-size: 0.85em;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
<h2>All Layers</h2>
|
|
86
|
+
<div class="layers-grid">
|
|
87
|
+
<div class="layer-card">
|
|
88
|
+
<h4>1. reset</h4>
|
|
89
|
+
<p>Lowest priority. Normalizes browser defaults.</p>
|
|
90
|
+
<code>sanitize.css</code><br>
|
|
91
|
+
<code>::selection</code><br>
|
|
92
|
+
<code>::backdrop</code>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="layer-card">
|
|
95
|
+
<h4>2. base</h4>
|
|
96
|
+
<p>Typography and HTML element styles.</p>
|
|
97
|
+
<code>typography.css</code><br>
|
|
98
|
+
<code>elements.css</code>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="layer-card">
|
|
101
|
+
<h4>3. utilities</h4>
|
|
102
|
+
<p>Reusable utility classes.</p>
|
|
103
|
+
<code>focus-ring.css</code><br>
|
|
104
|
+
<code>helpers.css</code>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="layer-card">
|
|
107
|
+
<h4>4. components</h4>
|
|
108
|
+
<p>All web component styles.</p>
|
|
109
|
+
<code>accordion</code>, <code>button</code>, <code>card</code>, etc.
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
`,
|
|
113
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
@import "./focus-ring.css";
|
|
3
|
+
@import "../../components/accordion/accordion.css";
|
|
4
|
+
@import "../../components/app-bar/app-bar.css";
|
|
5
|
+
@import "../../components/avatar/avatar.css";
|
|
6
|
+
@import "../../components/badge/badge.css";
|
|
7
|
+
@import "../../components/breadcrumb/breadcrumb.css";
|
|
8
|
+
@import "../../components/button/button.css";
|
|
9
|
+
@import "../../components/button-group/button-group.css";
|
|
10
|
+
@import "../../components/card/card.css";
|
|
11
|
+
@import "../../components/code-preview/code-preview.css";
|
|
12
|
+
@import "../../components/container/container.css";
|
|
13
|
+
@import "../../components/cta/cta.css";
|
|
14
|
+
@import "../../components/dialog/dialog.css";
|
|
15
|
+
@import "../../components/dropdown/dropdown.css";
|
|
16
|
+
@import "../../components/dropzone/dropzone.css";
|
|
17
|
+
@import "../../components/feature-list/feature-list.css";
|
|
18
|
+
@import "../../components/footer/footer.css";
|
|
19
|
+
@import "../../components/footer/footer-column.css";
|
|
20
|
+
@import "../../components/form-field/form-field.css";
|
|
21
|
+
@import "../../components/gallery/gallery.css";
|
|
22
|
+
@import "../../components/hero/hero.css";
|
|
23
|
+
@import "../../components/icon/icon.css";
|
|
24
|
+
@import "../../components/loading/loading.css";
|
|
25
|
+
@import "../../components/logo-cloud/logo-cloud.css";
|
|
26
|
+
@import "../../components/media-text/media-text.css";
|
|
27
|
+
@import "../../components/newsletter/newsletter.css";
|
|
28
|
+
@import "../../components/notice/notice.css";
|
|
29
|
+
@import "../../components/pagination/pagination.css";
|
|
30
|
+
@import "../../components/pricing/pricing.css";
|
|
31
|
+
@import "../../components/sidebar/sidebar.css";
|
|
32
|
+
@import "../../components/stats/stats.css";
|
|
33
|
+
@import "../../components/tabs/tabs.css";
|
|
34
|
+
@import "../../components/testimonials/testimonials.css";
|
|
35
|
+
@import "../../components/toast/toast.css";
|
|
36
|
+
@import "../../components/tooltip/tooltip.css";
|
|
37
|
+
}
|