@ghostly-ui/core 0.2.2 → 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.
Files changed (2) hide show
  1. package/dist/ghostly.css +76 -52
  2. 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: 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.
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 (override these to customize) --- */
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,53 +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
- /* Make inline elements (span, a, em, etc.) respect min-height/width */
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
- --_ghostly-display: inline-block;
75
+ display: inline-block;
78
76
  }
79
77
 
80
- /* ── Prevent text elements from collapsing horizontally ──
81
- Since text is transparent, the element's width comes from the text
82
- content. When content is empty ('') the element collapses to 0 width.
83
- We use min-width to prevent this. When real (visible) content is loaded,
84
- the content width will naturally exceed the min-width. */
85
- [data-ghostly] :where(h1) { min-width: 60%; }
86
- [data-ghostly] :where(h2) { min-width: 50%; }
87
- [data-ghostly] :where(h3) { min-width: 45%; }
88
- [data-ghostly] :where(h4, h5, h6) { min-width: 40%; }
89
- [data-ghostly] :where(p) { min-width: 80%; }
90
- [data-ghostly] :where(span, a, em, strong, small, label, time, code) { min-width: 3rem; }
91
- [data-ghostly] :where(button) { min-width: 5rem; }
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
+ ============================================================ */
84
+
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'; }
91
+
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'; }
94
+
95
+ /* Interactive: reasonable widths */
96
+ [data-ghostly] :where(button)::before { content: '\00a0\00a0\00a0\00a0\00a0\00a0\00a0\00a0'; }
92
97
  [data-ghostly] :where(input, textarea, select) { min-width: 6rem; }
93
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
+
94
108
  /* Headings: proportional min-height */
95
109
  [data-ghostly] :where(h1) { min-height: 1.75em; }
96
110
  [data-ghostly] :where(h2) { min-height: 1.5em; }
97
111
  [data-ghostly] :where(h3) { min-height: 1.3em; }
98
112
  [data-ghostly] :where(h4, h5, h6) { min-height: 1.15em; }
99
-
100
- /* Paragraphs: taller to simulate multi-line text */
101
- [data-ghostly] :where(p) { min-height: 3em; }
102
-
103
- /* Code blocks: taller */
113
+ [data-ghostly] :where(p) { min-height: 1em; }
104
114
  [data-ghostly] :where(pre) { min-height: 5em; }
105
-
106
- /* Interactive elements: reasonable minimums */
107
115
  [data-ghostly] :where(input, textarea, select) { min-height: 2.5rem; }
108
116
  [data-ghostly] :where(button) { min-height: 2.25rem; }
109
117
 
110
118
  /* ============================================================
111
- 3. MEDIA ELEMENTSHide content, show skeleton block
112
- Preserves ALL existing dimensions and border-radius.
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
113
142
  ============================================================ */
114
143
 
115
144
  [data-ghostly] :where(img, svg, video, canvas, picture, iframe) {
@@ -118,7 +147,6 @@
118
147
  box-shadow: none !important;
119
148
  }
120
149
 
121
- /* Hide image content without changing dimensions */
122
150
  [data-ghostly] :where(img) {
123
151
  color: transparent !important;
124
152
  object-position: -9999px !important;
@@ -128,23 +156,19 @@
128
156
  opacity: 0;
129
157
  }
130
158
 
131
- /* SVG: hide strokes/fills but keep size */
132
159
  [data-ghostly] :where(svg) {
133
160
  color: transparent !important;
134
161
  fill: transparent !important;
135
162
  stroke: transparent !important;
136
163
  }
137
164
 
138
- /* Only add min-size for SVG icons that might be empty */
139
165
  [data-ghostly] :where(svg:empty) {
140
166
  min-height: 1.5rem;
141
167
  min-width: 1.5rem;
142
168
  }
143
169
 
144
170
  /* ============================================================
145
- 4. EMPTY CONTAINERS — Style divs that act as placeholders
146
- When a component renders an empty div instead of an img
147
- (e.g. avatar placeholder), it should still show skeleton.
171
+ 6. EMPTY CONTAINERS — Skeleton background on placeholders
148
172
  ============================================================ */
149
173
 
150
174
  [data-ghostly] :where(div:empty) {
@@ -152,12 +176,9 @@
152
176
  }
153
177
 
154
178
  /* ============================================================
155
- 5. BORDER RADIUS — Use computed value, fallback to default
156
- Never override existing border-radius from the component.
179
+ 7. BORDER RADIUS — Lowest specificity, component CSS wins
157
180
  ============================================================ */
158
181
 
159
- /* Apply default radius ONLY to elements that don't have one set.
160
- We use a very low specificity so any component CSS wins. */
161
182
  @layer ghostly-defaults {
162
183
  [data-ghostly] h1, [data-ghostly] h2, [data-ghostly] h3,
163
184
  [data-ghostly] h4, [data-ghostly] h5, [data-ghostly] h6,
@@ -173,7 +194,7 @@
173
194
  }
174
195
 
175
196
  /* ============================================================
176
- 6. CUSTOM LINE COUNT — data-ghostly-lines="N"
197
+ 8. CUSTOM LINE COUNT — data-ghostly-lines="N"
177
198
  ============================================================ */
178
199
 
179
200
  [data-ghostly] :where([data-ghostly-lines="1"]) { min-height: 1em; }
@@ -186,7 +207,7 @@
186
207
  [data-ghostly] :where([data-ghostly-lines="8"]) { min-height: 8em; }
187
208
 
188
209
  /* ============================================================
189
- 7. CUSTOM ASPECT RATIO — data-ghostly-ratio
210
+ 9. CUSTOM ASPECT RATIO — data-ghostly-ratio
190
211
  ============================================================ */
191
212
 
192
213
  [data-ghostly] :where([data-ghostly-ratio="1/1"]) { aspect-ratio: 1/1 !important; }
@@ -197,7 +218,7 @@
197
218
  [data-ghostly] :where([data-ghostly-ratio="9/16"]) { aspect-ratio: 9/16 !important; }
198
219
 
199
220
  /* ============================================================
200
- 8. DECORATIVE — Strip visual noise
221
+ 10. DECORATIVE — Strip visual noise
201
222
  ============================================================ */
202
223
 
203
224
  [data-ghostly] :where(hr) {
@@ -205,7 +226,7 @@
205
226
  }
206
227
 
207
228
  /* ============================================================
208
- 9. EXCLUSIONS — data-ghostly-ignore restores original styles
229
+ 11. EXCLUSIONS — data-ghostly-ignore
209
230
  ============================================================ */
210
231
 
211
232
  [data-ghostly] [data-ghostly-ignore],
@@ -222,11 +243,15 @@
222
243
  animation: none !important;
223
244
  }
224
245
 
246
+ [data-ghostly] [data-ghostly-ignore]::before {
247
+ content: none !important;
248
+ }
249
+
225
250
  /* ============================================================
226
- 10. ANIMATIONS
251
+ 12. ANIMATIONS
227
252
  ============================================================ */
228
253
 
229
- /* --- Shimmer — gradient sweep left to right --- */
254
+ /* --- Shimmer --- */
230
255
 
231
256
  [data-ghostly='shimmer'] :where(
232
257
  h1, h2, h3, h4, h5, h6,
@@ -256,7 +281,7 @@
256
281
  100% { background-position: -100% 0; }
257
282
  }
258
283
 
259
- /* --- Pulse — opacity fade in/out --- */
284
+ /* --- Pulse --- */
260
285
 
261
286
  [data-ghostly='pulse'] :where(
262
287
  h1, h2, h3, h4, h5, h6,
@@ -277,7 +302,7 @@
277
302
  50% { opacity: 0.4; }
278
303
  }
279
304
 
280
- /* --- Wave — cascading pulse with stagger --- */
305
+ /* --- Wave --- */
281
306
 
282
307
  [data-ghostly='wave'] :where(
283
308
  h1, h2, h3, h4, h5, h6,
@@ -301,7 +326,6 @@
301
326
  100% { opacity: 1; }
302
327
  }
303
328
 
304
- /* Stagger direct children for cascading effect */
305
329
  [data-ghostly='wave'] > :nth-child(1) { animation-delay: 0ms !important; }
306
330
  [data-ghostly='wave'] > :nth-child(2) { animation-delay: 80ms !important; }
307
331
  [data-ghostly='wave'] > :nth-child(3) { animation-delay: 160ms !important; }
@@ -317,7 +341,7 @@
317
341
  [data-ghostly='wave'] > :nth-child(n+13) { animation-delay: 960ms !important; }
318
342
 
319
343
  /* ============================================================
320
- 11. SMOOTH TRANSITION — fade out when loading ends
344
+ 13. SMOOTH TRANSITION
321
345
  ============================================================ */
322
346
 
323
347
  [data-ghostly-smooth] :where(
@@ -338,7 +362,7 @@
338
362
  }
339
363
 
340
364
  /* ============================================================
341
- 12. ACCESSIBILITY
365
+ 14. ACCESSIBILITY
342
366
  ============================================================ */
343
367
 
344
368
  @media (prefers-reduced-motion: reduce) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostly-ui/core",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Zero-config skeleton loaders. Wrap your component, done.",
5
5
  "license": "MIT",
6
6
  "type": "module",