@ghostly-ui/core 0.2.1 → 0.2.3
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 +137 -92
- package/package.json +1 -1
package/dist/ghostly.css
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
GHOSTLY — Zero-config skeleton loaders
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Philosophy: NEVER override layout properties. Only hide content
|
|
5
|
+
and add skeleton background. The component's own CSS defines
|
|
6
|
+
all sizing, spacing, and border-radius.
|
|
6
7
|
============================================================ */
|
|
7
8
|
|
|
8
9
|
/* --- Custom properties (override these to customize) --- */
|
|
@@ -31,25 +32,7 @@
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/* ============================================================
|
|
34
|
-
1.
|
|
35
|
-
============================================================ */
|
|
36
|
-
|
|
37
|
-
/* All targetable elements inside a ghostly container */
|
|
38
|
-
[data-ghostly] :where(
|
|
39
|
-
h1, h2, h3, h4, h5, h6,
|
|
40
|
-
p, span, a, li, td, th, dt, dd,
|
|
41
|
-
label, legend, figcaption, caption, summary,
|
|
42
|
-
blockquote, cite, q, em, strong, small, mark, code, pre,
|
|
43
|
-
button, input, textarea, select, option,
|
|
44
|
-
fieldset, dialog, details, address,
|
|
45
|
-
time, abbr, sub, sup, del, ins,
|
|
46
|
-
img, svg, video, canvas, picture, iframe
|
|
47
|
-
) {
|
|
48
|
-
--_ghostly-target: 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/* ============================================================
|
|
52
|
-
2. CONTAINER — Blocks interaction while loading
|
|
35
|
+
1. CONTAINER — Blocks interaction while loading
|
|
53
36
|
============================================================ */
|
|
54
37
|
|
|
55
38
|
[data-ghostly] {
|
|
@@ -59,7 +42,8 @@
|
|
|
59
42
|
}
|
|
60
43
|
|
|
61
44
|
/* ============================================================
|
|
62
|
-
|
|
45
|
+
2. TEXT ELEMENTS — Hide text, show skeleton background
|
|
46
|
+
Only override visual properties. NEVER touch layout.
|
|
63
47
|
============================================================ */
|
|
64
48
|
|
|
65
49
|
[data-ghostly] :where(
|
|
@@ -74,81 +58,160 @@
|
|
|
74
58
|
color: transparent !important;
|
|
75
59
|
background-color: var(--ghostly-color) !important;
|
|
76
60
|
background-image: none !important;
|
|
77
|
-
border-radius: var(--ghostly-radius) !important;
|
|
78
61
|
border-color: transparent !important;
|
|
79
62
|
box-shadow: none !important;
|
|
80
63
|
text-decoration: none !important;
|
|
64
|
+
text-shadow: none !important;
|
|
81
65
|
outline: none !important;
|
|
66
|
+
/* Prevent empty elements from collapsing vertically */
|
|
82
67
|
min-height: 1em;
|
|
83
|
-
min-
|
|
68
|
+
/* Inline elements need display block/inline-block to respect min-height */
|
|
69
|
+
display: var(--_ghostly-display, revert);
|
|
84
70
|
}
|
|
85
71
|
|
|
86
|
-
/*
|
|
87
|
-
[data-ghostly] :where(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
[data-ghostly] :where(p) { min-height: 3em; min-width: 80%; }
|
|
72
|
+
/* Make inline elements (span, a, em, etc.) respect min-height/width */
|
|
73
|
+
[data-ghostly] :where(
|
|
74
|
+
span, a, em, strong, small, mark, code, abbr,
|
|
75
|
+
cite, q, sub, sup, del, ins, time, label
|
|
76
|
+
) {
|
|
77
|
+
--_ghostly-display: inline-block;
|
|
78
|
+
}
|
|
94
79
|
|
|
95
|
-
/*
|
|
96
|
-
|
|
80
|
+
/* ── Layout preservation ──
|
|
81
|
+
The critical problem: when text is empty, flex children collapse to 0 width.
|
|
82
|
+
Solution: force non-leaf containers to grow, and text elements to fill.
|
|
83
|
+
Uses !important to beat utility-class frameworks (Tailwind, etc). */
|
|
84
|
+
|
|
85
|
+
/* Non-leaf containers (divs wrapping text/images) must expand in flex/grid.
|
|
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
|
+
}
|
|
97
98
|
|
|
98
|
-
/*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
/* Leaf containers (empty divs used as placeholders like avatars)
|
|
100
|
+
should NOT grow — they have their own explicit dimensions. */
|
|
101
|
+
[data-ghostly] div:empty {
|
|
102
|
+
flex-grow: 0 !important;
|
|
103
|
+
flex-shrink: 0 !important;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
/*
|
|
105
|
-
[data-ghostly] :where(
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
/* Text elements: fill full width of their parent */
|
|
107
|
+
[data-ghostly] :where(h1, h2, h3, h4, h5, h6, p, pre,
|
|
108
|
+
blockquote, figcaption, caption, summary, address) {
|
|
109
|
+
width: 100%;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
/*
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
/* Inline-turned-block: reasonable minimums */
|
|
113
|
+
[data-ghostly] :where(span, a, em, strong, small, label, time, code) { min-width: 3rem; }
|
|
114
|
+
[data-ghostly] :where(button) { min-width: 5rem; }
|
|
115
|
+
[data-ghostly] :where(input, textarea, select) { min-width: 6rem; }
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
[data-ghostly] :where(
|
|
116
|
-
[data-ghostly] :where(
|
|
117
|
-
[data-ghostly] :where(
|
|
118
|
-
[data-ghostly] :where(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
[data-ghostly] :where(
|
|
117
|
+
/* Headings: proportional min-height */
|
|
118
|
+
[data-ghostly] :where(h1) { min-height: 1.75em; }
|
|
119
|
+
[data-ghostly] :where(h2) { min-height: 1.5em; }
|
|
120
|
+
[data-ghostly] :where(h3) { min-height: 1.3em; }
|
|
121
|
+
[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 */
|
|
127
|
+
[data-ghostly] :where(pre) { min-height: 5em; }
|
|
128
|
+
|
|
129
|
+
/* Interactive elements: reasonable minimums */
|
|
130
|
+
[data-ghostly] :where(input, textarea, select) { min-height: 2.5rem; }
|
|
131
|
+
[data-ghostly] :where(button) { min-height: 2.25rem; }
|
|
122
132
|
|
|
123
133
|
/* ============================================================
|
|
124
|
-
|
|
134
|
+
3. MEDIA ELEMENTS — Hide content, show skeleton block
|
|
135
|
+
Preserves ALL existing dimensions and border-radius.
|
|
125
136
|
============================================================ */
|
|
126
137
|
|
|
127
138
|
[data-ghostly] :where(img, svg, video, canvas, picture, iframe) {
|
|
128
|
-
color: transparent !important;
|
|
129
139
|
background-color: var(--ghostly-color) !important;
|
|
130
|
-
border-radius: var(--ghostly-radius) !important;
|
|
131
140
|
border-color: transparent !important;
|
|
132
141
|
box-shadow: none !important;
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
/* Hide
|
|
144
|
+
/* Hide image content without changing dimensions */
|
|
136
145
|
[data-ghostly] :where(img) {
|
|
137
|
-
|
|
146
|
+
color: transparent !important;
|
|
147
|
+
object-position: -9999px !important;
|
|
138
148
|
}
|
|
139
149
|
|
|
140
150
|
[data-ghostly] :where(video, iframe) {
|
|
141
151
|
opacity: 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* SVG: hide strokes/fills but keep size */
|
|
155
|
+
[data-ghostly] :where(svg) {
|
|
156
|
+
color: transparent !important;
|
|
157
|
+
fill: transparent !important;
|
|
158
|
+
stroke: transparent !important;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Only add min-size for SVG icons that might be empty */
|
|
162
|
+
[data-ghostly] :where(svg:empty) {
|
|
163
|
+
min-height: 1.5rem;
|
|
164
|
+
min-width: 1.5rem;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* ============================================================
|
|
168
|
+
4. EMPTY CONTAINERS — Style divs that act as placeholders
|
|
169
|
+
When a component renders an empty div instead of an img
|
|
170
|
+
(e.g. avatar placeholder), it should still show skeleton.
|
|
171
|
+
============================================================ */
|
|
172
|
+
|
|
173
|
+
[data-ghostly] :where(div:empty) {
|
|
142
174
|
background-color: var(--ghostly-color) !important;
|
|
143
175
|
}
|
|
144
176
|
|
|
145
|
-
/*
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
177
|
+
/* ============================================================
|
|
178
|
+
5. BORDER RADIUS — Use computed value, fallback to default
|
|
179
|
+
Never override existing border-radius from the component.
|
|
180
|
+
============================================================ */
|
|
181
|
+
|
|
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
|
+
@layer ghostly-defaults {
|
|
185
|
+
[data-ghostly] h1, [data-ghostly] h2, [data-ghostly] h3,
|
|
186
|
+
[data-ghostly] h4, [data-ghostly] h5, [data-ghostly] h6,
|
|
187
|
+
[data-ghostly] p, [data-ghostly] span, [data-ghostly] a,
|
|
188
|
+
[data-ghostly] li, [data-ghostly] td, [data-ghostly] th,
|
|
189
|
+
[data-ghostly] button, [data-ghostly] input,
|
|
190
|
+
[data-ghostly] textarea, [data-ghostly] select,
|
|
191
|
+
[data-ghostly] code, [data-ghostly] pre,
|
|
192
|
+
[data-ghostly] img, [data-ghostly] svg,
|
|
193
|
+
[data-ghostly] video, [data-ghostly] canvas {
|
|
194
|
+
border-radius: var(--ghostly-radius);
|
|
195
|
+
}
|
|
149
196
|
}
|
|
150
197
|
|
|
151
|
-
/*
|
|
198
|
+
/* ============================================================
|
|
199
|
+
6. CUSTOM LINE COUNT — data-ghostly-lines="N"
|
|
200
|
+
============================================================ */
|
|
201
|
+
|
|
202
|
+
[data-ghostly] :where([data-ghostly-lines="1"]) { min-height: 1em; }
|
|
203
|
+
[data-ghostly] :where([data-ghostly-lines="2"]) { min-height: 2em; }
|
|
204
|
+
[data-ghostly] :where([data-ghostly-lines="3"]) { min-height: 3em; }
|
|
205
|
+
[data-ghostly] :where([data-ghostly-lines="4"]) { min-height: 4em; }
|
|
206
|
+
[data-ghostly] :where([data-ghostly-lines="5"]) { min-height: 5em; }
|
|
207
|
+
[data-ghostly] :where([data-ghostly-lines="6"]) { min-height: 6em; }
|
|
208
|
+
[data-ghostly] :where([data-ghostly-lines="7"]) { min-height: 7em; }
|
|
209
|
+
[data-ghostly] :where([data-ghostly-lines="8"]) { min-height: 8em; }
|
|
210
|
+
|
|
211
|
+
/* ============================================================
|
|
212
|
+
7. CUSTOM ASPECT RATIO — data-ghostly-ratio
|
|
213
|
+
============================================================ */
|
|
214
|
+
|
|
152
215
|
[data-ghostly] :where([data-ghostly-ratio="1/1"]) { aspect-ratio: 1/1 !important; }
|
|
153
216
|
[data-ghostly] :where([data-ghostly-ratio="4/3"]) { aspect-ratio: 4/3 !important; }
|
|
154
217
|
[data-ghostly] :where([data-ghostly-ratio="16/9"]) { aspect-ratio: 16/9 !important; }
|
|
@@ -156,35 +219,16 @@
|
|
|
156
219
|
[data-ghostly] :where([data-ghostly-ratio="3/4"]) { aspect-ratio: 3/4 !important; }
|
|
157
220
|
[data-ghostly] :where([data-ghostly-ratio="9/16"]) { aspect-ratio: 9/16 !important; }
|
|
158
221
|
|
|
159
|
-
/* SVG icons (typically small) */
|
|
160
|
-
[data-ghostly] :where(svg) {
|
|
161
|
-
min-height: 1.5rem;
|
|
162
|
-
min-width: 1.5rem;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
222
|
/* ============================================================
|
|
166
|
-
|
|
223
|
+
8. DECORATIVE — Strip visual noise
|
|
167
224
|
============================================================ */
|
|
168
225
|
|
|
169
226
|
[data-ghostly] :where(hr) {
|
|
170
227
|
border-color: var(--ghostly-color) !important;
|
|
171
228
|
}
|
|
172
229
|
|
|
173
|
-
[data-ghostly] :where(
|
|
174
|
-
[class*="badge"],
|
|
175
|
-
[class*="chip"],
|
|
176
|
-
[class*="tag"],
|
|
177
|
-
[class*="avatar"]
|
|
178
|
-
) {
|
|
179
|
-
color: transparent !important;
|
|
180
|
-
background-color: var(--ghostly-color) !important;
|
|
181
|
-
background-image: none !important;
|
|
182
|
-
border-color: transparent !important;
|
|
183
|
-
box-shadow: none !important;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
230
|
/* ============================================================
|
|
187
|
-
|
|
231
|
+
9. EXCLUSIONS — data-ghostly-ignore restores original styles
|
|
188
232
|
============================================================ */
|
|
189
233
|
|
|
190
234
|
[data-ghostly] [data-ghostly-ignore],
|
|
@@ -198,17 +242,15 @@
|
|
|
198
242
|
pointer-events: auto;
|
|
199
243
|
user-select: auto;
|
|
200
244
|
min-height: initial;
|
|
201
|
-
min-width: initial;
|
|
202
245
|
animation: none !important;
|
|
203
246
|
}
|
|
204
247
|
|
|
205
248
|
/* ============================================================
|
|
206
|
-
|
|
249
|
+
10. ANIMATIONS
|
|
207
250
|
============================================================ */
|
|
208
251
|
|
|
209
252
|
/* --- Shimmer — gradient sweep left to right --- */
|
|
210
253
|
|
|
211
|
-
[data-ghostly='shimmer'] :where([style*="--_ghostly-target"]),
|
|
212
254
|
[data-ghostly='shimmer'] :where(
|
|
213
255
|
h1, h2, h3, h4, h5, h6,
|
|
214
256
|
p, span, a, li, td, th, dt, dd,
|
|
@@ -217,7 +259,8 @@
|
|
|
217
259
|
button, input, textarea, select, option,
|
|
218
260
|
fieldset, dialog, details, address,
|
|
219
261
|
time, abbr, sub, sup, del, ins,
|
|
220
|
-
img, svg, video, canvas, picture, iframe
|
|
262
|
+
img, svg, video, canvas, picture, iframe,
|
|
263
|
+
div:empty
|
|
221
264
|
) {
|
|
222
265
|
background: linear-gradient(
|
|
223
266
|
90deg,
|
|
@@ -246,7 +289,8 @@
|
|
|
246
289
|
button, input, textarea, select, option,
|
|
247
290
|
fieldset, dialog, details, address,
|
|
248
291
|
time, abbr, sub, sup, del, ins,
|
|
249
|
-
img, svg, video, canvas, picture, iframe
|
|
292
|
+
img, svg, video, canvas, picture, iframe,
|
|
293
|
+
div:empty
|
|
250
294
|
) {
|
|
251
295
|
animation: ghostly-pulse var(--ghostly-speed) ease-in-out infinite !important;
|
|
252
296
|
}
|
|
@@ -266,7 +310,8 @@
|
|
|
266
310
|
button, input, textarea, select, option,
|
|
267
311
|
fieldset, dialog, details, address,
|
|
268
312
|
time, abbr, sub, sup, del, ins,
|
|
269
|
-
img, svg, video, canvas, picture, iframe
|
|
313
|
+
img, svg, video, canvas, picture, iframe,
|
|
314
|
+
div:empty
|
|
270
315
|
) {
|
|
271
316
|
animation: ghostly-wave var(--ghostly-speed) ease-in-out infinite !important;
|
|
272
317
|
}
|
|
@@ -295,7 +340,7 @@
|
|
|
295
340
|
[data-ghostly='wave'] > :nth-child(n+13) { animation-delay: 960ms !important; }
|
|
296
341
|
|
|
297
342
|
/* ============================================================
|
|
298
|
-
|
|
343
|
+
11. SMOOTH TRANSITION — fade out when loading ends
|
|
299
344
|
============================================================ */
|
|
300
345
|
|
|
301
346
|
[data-ghostly-smooth] :where(
|
|
@@ -316,7 +361,7 @@
|
|
|
316
361
|
}
|
|
317
362
|
|
|
318
363
|
/* ============================================================
|
|
319
|
-
|
|
364
|
+
12. ACCESSIBILITY
|
|
320
365
|
============================================================ */
|
|
321
366
|
|
|
322
367
|
@media (prefers-reduced-motion: reduce) {
|