@ghostly-ui/core 0.2.3 → 0.2.4
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 +73 -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,81 @@
|
|
|
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
|
+
/* Inline elements need inline-block to respect min-height */
|
|
73
71
|
[data-ghostly] :where(
|
|
74
72
|
span, a, em, strong, small, mark, code, abbr,
|
|
75
73
|
cite, q, sub, sup, del, ins, time, label
|
|
76
74
|
) {
|
|
77
|
-
|
|
75
|
+
display: inline-block;
|
|
78
76
|
}
|
|
79
77
|
|
|
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
|
-
}
|
|
78
|
+
/* ============================================================
|
|
79
|
+
3. PREVENT TEXT COLLAPSE — Invisible pseudo-content
|
|
80
|
+
When text is empty (''), elements collapse to 0 width.
|
|
81
|
+
We inject invisible non-breaking spaces via ::before so the
|
|
82
|
+
element has natural width. Works with ANY layout system.
|
|
83
|
+
============================================================ */
|
|
98
84
|
|
|
99
|
-
/*
|
|
100
|
-
|
|
101
|
-
[data-ghostly]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
85
|
+
/* Block text: inject wide invisible content */
|
|
86
|
+
[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'; }
|
|
87
|
+
[data-ghostly] :where(h2)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
88
|
+
[data-ghostly] :where(h3)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
89
|
+
[data-ghostly] :where(h4, h5, h6)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
90
|
+
[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
91
|
|
|
106
|
-
/*
|
|
107
|
-
[data-ghostly] :where(
|
|
108
|
-
blockquote, figcaption, caption, summary, address) {
|
|
109
|
-
width: 100%;
|
|
110
|
-
}
|
|
92
|
+
/* Inline text: shorter content */
|
|
93
|
+
[data-ghostly] :where(span, a, em, strong, small, label, time, code)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
111
94
|
|
|
112
|
-
/*
|
|
113
|
-
[data-ghostly] :where(
|
|
114
|
-
[data-ghostly] :where(button) { min-width: 5rem; }
|
|
95
|
+
/* Interactive: reasonable widths */
|
|
96
|
+
[data-ghostly] :where(button)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
|
|
115
97
|
[data-ghostly] :where(input, textarea, select) { min-width: 6rem; }
|
|
116
98
|
|
|
99
|
+
/* All pseudo-content inherits transparent color so it's invisible */
|
|
100
|
+
[data-ghostly] :where(h1, h2, h3, h4, h5, h6, p, span, a, li,
|
|
101
|
+
em, strong, small, mark, code, label, time, button)::before {
|
|
102
|
+
color: transparent !important;
|
|
103
|
+
visibility: hidden;
|
|
104
|
+
display: inline;
|
|
105
|
+
height: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
117
108
|
/* Headings: proportional min-height */
|
|
118
109
|
[data-ghostly] :where(h1) { min-height: 1.75em; }
|
|
119
110
|
[data-ghostly] :where(h2) { min-height: 1.5em; }
|
|
120
111
|
[data-ghostly] :where(h3) { min-height: 1.3em; }
|
|
121
112
|
[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 */
|
|
113
|
+
[data-ghostly] :where(p) { min-height: 1em; }
|
|
127
114
|
[data-ghostly] :where(pre) { min-height: 5em; }
|
|
128
|
-
|
|
129
|
-
/* Interactive elements: reasonable minimums */
|
|
130
115
|
[data-ghostly] :where(input, textarea, select) { min-height: 2.5rem; }
|
|
131
116
|
[data-ghostly] :where(button) { min-height: 2.25rem; }
|
|
132
117
|
|
|
133
118
|
/* ============================================================
|
|
134
|
-
|
|
135
|
-
|
|
119
|
+
4. FLEX/GRID LAYOUT FIX — Surgical, using :has()
|
|
120
|
+
Only expand containers that CONTAIN text elements.
|
|
121
|
+
Empty divs (avatars, icons) are left alone.
|
|
122
|
+
This is safe for sidebars, fixed columns, etc.
|
|
123
|
+
============================================================ */
|
|
124
|
+
|
|
125
|
+
/* Containers holding text elements should grow in flex layouts.
|
|
126
|
+
:has() ensures we ONLY target divs that wrap text, not fixed-size ones. */
|
|
127
|
+
[data-ghostly] :where(
|
|
128
|
+
div, section, article, aside, header, footer, nav, main
|
|
129
|
+
):has(> :where(h1, h2, h3, h4, h5, h6, p, span, a, button, input, label)) {
|
|
130
|
+
flex-grow: 1;
|
|
131
|
+
min-width: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Empty containers (avatar placeholders etc): NEVER grow */
|
|
135
|
+
[data-ghostly] :where(div:empty, span:empty) {
|
|
136
|
+
flex-grow: 0 !important;
|
|
137
|
+
flex-shrink: 0 !important;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ============================================================
|
|
141
|
+
5. MEDIA ELEMENTS — Hide content, preserve dimensions
|
|
136
142
|
============================================================ */
|
|
137
143
|
|
|
138
144
|
[data-ghostly] :where(img, svg, video, canvas, picture, iframe) {
|
|
@@ -141,7 +147,6 @@
|
|
|
141
147
|
box-shadow: none !important;
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
/* Hide image content without changing dimensions */
|
|
145
150
|
[data-ghostly] :where(img) {
|
|
146
151
|
color: transparent !important;
|
|
147
152
|
object-position: -9999px !important;
|
|
@@ -151,23 +156,19 @@
|
|
|
151
156
|
opacity: 0;
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
/* SVG: hide strokes/fills but keep size */
|
|
155
159
|
[data-ghostly] :where(svg) {
|
|
156
160
|
color: transparent !important;
|
|
157
161
|
fill: transparent !important;
|
|
158
162
|
stroke: transparent !important;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
/* Only add min-size for SVG icons that might be empty */
|
|
162
165
|
[data-ghostly] :where(svg:empty) {
|
|
163
166
|
min-height: 1.5rem;
|
|
164
167
|
min-width: 1.5rem;
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
/* ============================================================
|
|
168
|
-
|
|
169
|
-
When a component renders an empty div instead of an img
|
|
170
|
-
(e.g. avatar placeholder), it should still show skeleton.
|
|
171
|
+
6. EMPTY CONTAINERS — Skeleton background on placeholders
|
|
171
172
|
============================================================ */
|
|
172
173
|
|
|
173
174
|
[data-ghostly] :where(div:empty) {
|
|
@@ -175,12 +176,9 @@
|
|
|
175
176
|
}
|
|
176
177
|
|
|
177
178
|
/* ============================================================
|
|
178
|
-
|
|
179
|
-
Never override existing border-radius from the component.
|
|
179
|
+
7. BORDER RADIUS — Lowest specificity, component CSS wins
|
|
180
180
|
============================================================ */
|
|
181
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
182
|
@layer ghostly-defaults {
|
|
185
183
|
[data-ghostly] h1, [data-ghostly] h2, [data-ghostly] h3,
|
|
186
184
|
[data-ghostly] h4, [data-ghostly] h5, [data-ghostly] h6,
|
|
@@ -196,7 +194,7 @@
|
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
/* ============================================================
|
|
199
|
-
|
|
197
|
+
8. CUSTOM LINE COUNT — data-ghostly-lines="N"
|
|
200
198
|
============================================================ */
|
|
201
199
|
|
|
202
200
|
[data-ghostly] :where([data-ghostly-lines="1"]) { min-height: 1em; }
|
|
@@ -209,7 +207,7 @@
|
|
|
209
207
|
[data-ghostly] :where([data-ghostly-lines="8"]) { min-height: 8em; }
|
|
210
208
|
|
|
211
209
|
/* ============================================================
|
|
212
|
-
|
|
210
|
+
9. CUSTOM ASPECT RATIO — data-ghostly-ratio
|
|
213
211
|
============================================================ */
|
|
214
212
|
|
|
215
213
|
[data-ghostly] :where([data-ghostly-ratio="1/1"]) { aspect-ratio: 1/1 !important; }
|
|
@@ -220,7 +218,7 @@
|
|
|
220
218
|
[data-ghostly] :where([data-ghostly-ratio="9/16"]) { aspect-ratio: 9/16 !important; }
|
|
221
219
|
|
|
222
220
|
/* ============================================================
|
|
223
|
-
|
|
221
|
+
10. DECORATIVE — Strip visual noise
|
|
224
222
|
============================================================ */
|
|
225
223
|
|
|
226
224
|
[data-ghostly] :where(hr) {
|
|
@@ -228,7 +226,7 @@
|
|
|
228
226
|
}
|
|
229
227
|
|
|
230
228
|
/* ============================================================
|
|
231
|
-
|
|
229
|
+
11. EXCLUSIONS — data-ghostly-ignore
|
|
232
230
|
============================================================ */
|
|
233
231
|
|
|
234
232
|
[data-ghostly] [data-ghostly-ignore],
|
|
@@ -245,11 +243,15 @@
|
|
|
245
243
|
animation: none !important;
|
|
246
244
|
}
|
|
247
245
|
|
|
246
|
+
[data-ghostly] [data-ghostly-ignore]::before {
|
|
247
|
+
content: none !important;
|
|
248
|
+
}
|
|
249
|
+
|
|
248
250
|
/* ============================================================
|
|
249
|
-
|
|
251
|
+
12. ANIMATIONS
|
|
250
252
|
============================================================ */
|
|
251
253
|
|
|
252
|
-
/* --- Shimmer
|
|
254
|
+
/* --- Shimmer --- */
|
|
253
255
|
|
|
254
256
|
[data-ghostly='shimmer'] :where(
|
|
255
257
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -279,7 +281,7 @@
|
|
|
279
281
|
100% { background-position: -100% 0; }
|
|
280
282
|
}
|
|
281
283
|
|
|
282
|
-
/* --- Pulse
|
|
284
|
+
/* --- Pulse --- */
|
|
283
285
|
|
|
284
286
|
[data-ghostly='pulse'] :where(
|
|
285
287
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -300,7 +302,7 @@
|
|
|
300
302
|
50% { opacity: 0.4; }
|
|
301
303
|
}
|
|
302
304
|
|
|
303
|
-
/* --- Wave
|
|
305
|
+
/* --- Wave --- */
|
|
304
306
|
|
|
305
307
|
[data-ghostly='wave'] :where(
|
|
306
308
|
h1, h2, h3, h4, h5, h6,
|
|
@@ -324,7 +326,6 @@
|
|
|
324
326
|
100% { opacity: 1; }
|
|
325
327
|
}
|
|
326
328
|
|
|
327
|
-
/* Stagger direct children for cascading effect */
|
|
328
329
|
[data-ghostly='wave'] > :nth-child(1) { animation-delay: 0ms !important; }
|
|
329
330
|
[data-ghostly='wave'] > :nth-child(2) { animation-delay: 80ms !important; }
|
|
330
331
|
[data-ghostly='wave'] > :nth-child(3) { animation-delay: 160ms !important; }
|
|
@@ -340,7 +341,7 @@
|
|
|
340
341
|
[data-ghostly='wave'] > :nth-child(n+13) { animation-delay: 960ms !important; }
|
|
341
342
|
|
|
342
343
|
/* ============================================================
|
|
343
|
-
|
|
344
|
+
13. SMOOTH TRANSITION
|
|
344
345
|
============================================================ */
|
|
345
346
|
|
|
346
347
|
[data-ghostly-smooth] :where(
|
|
@@ -361,7 +362,7 @@
|
|
|
361
362
|
}
|
|
362
363
|
|
|
363
364
|
/* ============================================================
|
|
364
|
-
|
|
365
|
+
14. ACCESSIBILITY
|
|
365
366
|
============================================================ */
|
|
366
367
|
|
|
367
368
|
@media (prefers-reduced-motion: reduce) {
|