@ghostly-ui/core 0.2.3 → 0.2.5
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/dist/ghostly.css +79 -72
- package/package.json +1 -1
package/dist/ghostly.css
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
GHOSTLY — Zero-config skeleton loaders
|
|
3
3
|
|
|
4
|
-
Philosophy:
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Philosophy:
|
|
5
|
+
1. NEVER override layout properties blindly
|
|
6
|
+
2. Only hide content + add skeleton background
|
|
7
|
+
3. Use :has() to surgically fix collapsing flex children
|
|
8
|
+
4. Component's own CSS defines sizing, spacing, and radius
|
|
7
9
|
============================================================ */
|
|
8
10
|
|
|
9
|
-
/* --- Custom properties
|
|
11
|
+
/* --- Custom properties --- */
|
|
10
12
|
|
|
11
13
|
:root {
|
|
12
14
|
--ghostly-color: hsl(220 13% 87%);
|
|
@@ -43,7 +45,6 @@
|
|
|
43
45
|
|
|
44
46
|
/* ============================================================
|
|
45
47
|
2. TEXT ELEMENTS — Hide text, show skeleton background
|
|
46
|
-
Only override visual properties. NEVER touch layout.
|
|
47
48
|
============================================================ */
|
|
48
49
|
|
|
49
50
|
[data-ghostly] :where(
|
|
@@ -63,76 +64,87 @@
|
|
|
63
64
|
text-decoration: none !important;
|
|
64
65
|
text-shadow: none !important;
|
|
65
66
|
outline: none !important;
|
|
66
|
-
/* Prevent empty elements from collapsing vertically */
|
|
67
67
|
min-height: 1em;
|
|
68
|
-
/* Inline elements need display block/inline-block to respect min-height */
|
|
69
|
-
display: var(--_ghostly-display, revert);
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
/*
|
|
70
|
+
/* Hide placeholders and ::before pseudo-content visibility */
|
|
71
|
+
[data-ghostly] :where(input, textarea)::placeholder {
|
|
72
|
+
color: transparent !important;
|
|
73
|
+
opacity: 0 !important;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Inline elements need inline-block to respect min-height */
|
|
73
77
|
[data-ghostly] :where(
|
|
74
78
|
span, a, em, strong, small, mark, code, abbr,
|
|
75
79
|
cite, q, sub, sup, del, ins, time, label
|
|
76
80
|
) {
|
|
77
|
-
|
|
81
|
+
display: inline-block;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
/*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
flex-grow:1 makes them fill available space instead of collapsing. */
|
|
87
|
-
[data-ghostly] div,
|
|
88
|
-
[data-ghostly] section,
|
|
89
|
-
[data-ghostly] article,
|
|
90
|
-
[data-ghostly] main,
|
|
91
|
-
[data-ghostly] aside,
|
|
92
|
-
[data-ghostly] header,
|
|
93
|
-
[data-ghostly] footer,
|
|
94
|
-
[data-ghostly] nav {
|
|
95
|
-
flex-grow: 1 !important;
|
|
96
|
-
min-width: 0 !important;
|
|
97
|
-
}
|
|
84
|
+
/* ============================================================
|
|
85
|
+
3. PREVENT TEXT COLLAPSE — Invisible pseudo-content
|
|
86
|
+
When text is empty (''), elements collapse to 0 width.
|
|
87
|
+
We inject invisible non-breaking spaces via ::before so the
|
|
88
|
+
element has natural width. Works with ANY layout system.
|
|
89
|
+
============================================================ */
|
|
98
90
|
|
|
99
|
-
/*
|
|
100
|
-
|
|
101
|
-
[data-ghostly]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
91
|
+
/* Block text: inject wide invisible content */
|
|
92
|
+
[data-ghostly] :where(h1)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
93
|
+
[data-ghostly] :where(h2)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
94
|
+
[data-ghostly] :where(h3)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
95
|
+
[data-ghostly] :where(h4, h5, h6)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
96
|
+
[data-ghostly] :where(p)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
105
97
|
|
|
106
|
-
/*
|
|
107
|
-
[data-ghostly] :where(
|
|
108
|
-
blockquote, figcaption, caption, summary, address) {
|
|
109
|
-
width: 100%;
|
|
110
|
-
}
|
|
98
|
+
/* Inline text: shorter content */
|
|
99
|
+
[data-ghostly] :where(span, a, em, strong, small, label, time, code)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
111
100
|
|
|
112
|
-
/*
|
|
113
|
-
[data-ghostly] :where(
|
|
114
|
-
[data-ghostly] :where(button) { min-width: 5rem; }
|
|
101
|
+
/* Interactive: reasonable widths */
|
|
102
|
+
[data-ghostly] :where(button)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
115
103
|
[data-ghostly] :where(input, textarea, select) { min-width: 6rem; }
|
|
116
104
|
|
|
105
|
+
/* All pseudo-content inherits transparent color so it's invisible */
|
|
106
|
+
[data-ghostly] :where(h1, h2, h3, h4, h5, h6, p, span, a, li,
|
|
107
|
+
em, strong, small, mark, code, label, time, button)::before {
|
|
108
|
+
color: transparent !important;
|
|
109
|
+
visibility: hidden;
|
|
110
|
+
display: inline;
|
|
111
|
+
height: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
117
114
|
/* Headings: proportional min-height */
|
|
118
115
|
[data-ghostly] :where(h1) { min-height: 1.75em; }
|
|
119
116
|
[data-ghostly] :where(h2) { min-height: 1.5em; }
|
|
120
117
|
[data-ghostly] :where(h3) { min-height: 1.3em; }
|
|
121
118
|
[data-ghostly] :where(h4, h5, h6) { min-height: 1.15em; }
|
|
122
|
-
|
|
123
|
-
/* Paragraphs: taller to simulate multi-line text */
|
|
124
|
-
[data-ghostly] :where(p) { min-height: 3em; }
|
|
125
|
-
|
|
126
|
-
/* Code blocks: taller */
|
|
119
|
+
[data-ghostly] :where(p) { min-height: 1em; }
|
|
127
120
|
[data-ghostly] :where(pre) { min-height: 5em; }
|
|
128
|
-
|
|
129
|
-
/* Interactive elements: reasonable minimums */
|
|
130
121
|
[data-ghostly] :where(input, textarea, select) { min-height: 2.5rem; }
|
|
131
122
|
[data-ghostly] :where(button) { min-height: 2.25rem; }
|
|
132
123
|
|
|
133
124
|
/* ============================================================
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
4. FLEX/GRID LAYOUT FIX — Surgical, using :has()
|
|
126
|
+
Only expand containers that CONTAIN text elements.
|
|
127
|
+
Empty divs (avatars, icons) are left alone.
|
|
128
|
+
This is safe for sidebars, fixed columns, etc.
|
|
129
|
+
============================================================ */
|
|
130
|
+
|
|
131
|
+
/* Containers holding text elements should grow in flex layouts.
|
|
132
|
+
:has() ensures we ONLY target divs that wrap text, not fixed-size ones. */
|
|
133
|
+
[data-ghostly] :where(
|
|
134
|
+
div, section, article, aside, header, footer, nav, main
|
|
135
|
+
):has(> :where(h1, h2, h3, h4, h5, h6, p, span, a, button, input, label)) {
|
|
136
|
+
flex-grow: 1;
|
|
137
|
+
min-width: 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Empty containers (avatar placeholders etc): NEVER grow */
|
|
141
|
+
[data-ghostly] :where(div:empty, span:empty) {
|
|
142
|
+
flex-grow: 0 !important;
|
|
143
|
+
flex-shrink: 0 !important;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ============================================================
|
|
147
|
+
5. MEDIA ELEMENTS — Hide content, preserve dimensions
|
|
136
148
|
============================================================ */
|
|
137
149
|
|
|
138
150
|
[data-ghostly] :where(img, svg, video, canvas, picture, iframe) {
|
|
@@ -141,7 +153,6 @@
|
|
|
141
153
|
box-shadow: none !important;
|
|
142
154
|
}
|
|
143
155
|
|
|
144
|
-
/* Hide image content without changing dimensions */
|
|
145
156
|
[data-ghostly] :where(img) {
|
|
146
157
|
color: transparent !important;
|
|
147
158
|
object-position: -9999px !important;
|
|
@@ -151,23 +162,19 @@
|
|
|
151
162
|
opacity: 0;
|
|
152
163
|
}
|
|
153
164
|
|
|
154
|
-
/* SVG: hide strokes/fills but keep size */
|
|
155
165
|
[data-ghostly] :where(svg) {
|
|
156
166
|
color: transparent !important;
|
|
157
167
|
fill: transparent !important;
|
|
158
168
|
stroke: transparent !important;
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
/* Only add min-size for SVG icons that might be empty */
|
|
162
171
|
[data-ghostly] :where(svg:empty) {
|
|
163
172
|
min-height: 1.5rem;
|
|
164
173
|
min-width: 1.5rem;
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
/* ============================================================
|
|
168
|
-
|
|
169
|
-
When a component renders an empty div instead of an img
|
|
170
|
-
(e.g. avatar placeholder), it should still show skeleton.
|
|
177
|
+
6. EMPTY CONTAINERS — Skeleton background on placeholders
|
|
171
178
|
============================================================ */
|
|
172
179
|
|
|
173
180
|
[data-ghostly] :where(div:empty) {
|
|
@@ -175,12 +182,9 @@
|
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
/* ============================================================
|
|
178
|
-
|
|
179
|
-
Never override existing border-radius from the component.
|
|
185
|
+
7. BORDER RADIUS — Lowest specificity, component CSS wins
|
|
180
186
|
============================================================ */
|
|
181
187
|
|
|
182
|
-
/* Apply default radius ONLY to elements that don't have one set.
|
|
183
|
-
We use a very low specificity so any component CSS wins. */
|
|
184
188
|
@layer ghostly-defaults {
|
|
185
189
|
[data-ghostly] h1, [data-ghostly] h2, [data-ghostly] h3,
|
|
186
190
|
[data-ghostly] h4, [data-ghostly] h5, [data-ghostly] h6,
|
|
@@ -196,7 +200,7 @@
|
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
/* ============================================================
|
|
199
|
-
|
|
203
|
+
8. CUSTOM LINE COUNT — data-ghostly-lines="N"
|
|
200
204
|
============================================================ */
|
|
201
205
|
|
|
202
206
|
[data-ghostly] :where([data-ghostly-lines="1"]) { min-height: 1em; }
|
|
@@ -209,7 +213,7 @@
|
|
|
209
213
|
[data-ghostly] :where([data-ghostly-lines="8"]) { min-height: 8em; }
|
|
210
214
|
|
|
211
215
|
/* ============================================================
|
|
212
|
-
|
|
216
|
+
9. CUSTOM ASPECT RATIO — data-ghostly-ratio
|
|
213
217
|
============================================================ */
|
|
214
218
|
|
|
215
219
|
[data-ghostly] :where([data-ghostly-ratio="1/1"]) { aspect-ratio: 1/1 !important; }
|
|
@@ -220,7 +224,7 @@
|
|
|
220
224
|
[data-ghostly] :where([data-ghostly-ratio="9/16"]) { aspect-ratio: 9/16 !important; }
|
|
221
225
|
|
|
222
226
|
/* ============================================================
|
|
223
|
-
|
|
227
|
+
10. DECORATIVE — Strip visual noise
|
|
224
228
|
============================================================ */
|
|
225
229
|
|
|
226
230
|
[data-ghostly] :where(hr) {
|
|
@@ -228,7 +232,7 @@
|
|
|
228
232
|
}
|
|
229
233
|
|
|
230
234
|
/* ============================================================
|
|
231
|
-
|
|
235
|
+
11. EXCLUSIONS — data-ghostly-ignore
|
|
232
236
|
============================================================ */
|
|
233
237
|
|
|
234
238
|
[data-ghostly] [data-ghostly-ignore],
|
|
@@ -245,11 +249,15 @@
|
|
|
245
249
|
animation: none !important;
|
|
246
250
|
}
|
|
247
251
|
|
|
252
|
+
[data-ghostly] [data-ghostly-ignore]::before {
|
|
253
|
+
content: none !important;
|
|
254
|
+
}
|
|
255
|
+
|
|
248
256
|
/* ============================================================
|
|
249
|
-
|
|
257
|
+
12. ANIMATIONS
|
|
250
258
|
============================================================ */
|
|
251
259
|
|
|
252
|
-
/* --- Shimmer
|
|
260
|
+
/* --- Shimmer --- */
|
|
253
261
|
|
|
254
262
|
[data-ghostly='shimmer'] :where(
|
|
255
263
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -279,7 +287,7 @@
|
|
|
279
287
|
100% { background-position: -100% 0; }
|
|
280
288
|
}
|
|
281
289
|
|
|
282
|
-
/* --- Pulse
|
|
290
|
+
/* --- Pulse --- */
|
|
283
291
|
|
|
284
292
|
[data-ghostly='pulse'] :where(
|
|
285
293
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -300,7 +308,7 @@
|
|
|
300
308
|
50% { opacity: 0.4; }
|
|
301
309
|
}
|
|
302
310
|
|
|
303
|
-
/* --- Wave
|
|
311
|
+
/* --- Wave --- */
|
|
304
312
|
|
|
305
313
|
[data-ghostly='wave'] :where(
|
|
306
314
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -324,7 +332,6 @@
|
|
|
324
332
|
100% { opacity: 1; }
|
|
325
333
|
}
|
|
326
334
|
|
|
327
|
-
/* Stagger direct children for cascading effect */
|
|
328
335
|
[data-ghostly='wave'] > :nth-child(1) { animation-delay: 0ms !important; }
|
|
329
336
|
[data-ghostly='wave'] > :nth-child(2) { animation-delay: 80ms !important; }
|
|
330
337
|
[data-ghostly='wave'] > :nth-child(3) { animation-delay: 160ms !important; }
|
|
@@ -340,7 +347,7 @@
|
|
|
340
347
|
[data-ghostly='wave'] > :nth-child(n+13) { animation-delay: 960ms !important; }
|
|
341
348
|
|
|
342
349
|
/* ============================================================
|
|
343
|
-
|
|
350
|
+
13. SMOOTH TRANSITION
|
|
344
351
|
============================================================ */
|
|
345
352
|
|
|
346
353
|
[data-ghostly-smooth] :where(
|
|
@@ -361,7 +368,7 @@
|
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
/* ============================================================
|
|
364
|
-
|
|
371
|
+
14. ACCESSIBILITY
|
|
365
372
|
============================================================ */
|
|
366
373
|
|
|
367
374
|
@media (prefers-reduced-motion: reduce) {
|