@kubex/zinc 1.0.107 → 1.0.108
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/custom-elements.json +49 -1
- package/dist/vscode.html-custom-data.json +2 -0
- package/dist/web-types.json +11 -1
- package/dist/zn.d.ts +2 -0
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +41 -27
- package/docs/_includes/sidebar.njk +1 -0
- package/docs/pages/getting-started/shimmer-text.md +81 -0
- package/package.json +1 -1
- package/scss/shared/index.scss +1 -0
- package/scss/shared/shimmer.scss +98 -0
- package/src/components/markdown-editor/markdown-editor.component.ts +21 -0
- package/src/components/markdown-editor/markdown-editor.scss +24 -1
- package/src/components/tabs/tabs.component.ts +6 -2
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<li><a href="/getting-started/table-examples">Table Examples</a></li>
|
|
8
8
|
<li><a href="/getting-started/example-layout">Example Layout</a></li>
|
|
9
9
|
<li><a href="/getting-started/example-page">Example Page</a></li>
|
|
10
|
+
<li><a href="/getting-started/shimmer-text">Shimmer Text</a></li>
|
|
10
11
|
</ul>
|
|
11
12
|
</li>
|
|
12
13
|
<li>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
meta:
|
|
3
|
+
title: Shimmer Text
|
|
4
|
+
description: A global utility class that applies an animated shimmer sweep to text, suitable for loading, processing, or "thinking" states.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Shimmer Text
|
|
8
|
+
|
|
9
|
+
`shimmer-text` is a global utility class that animates a horizontal highlight sweep across text colour. Use it to convey an in-progress state — AI generation, background processing, or any "working on it" indicator.
|
|
10
|
+
|
|
11
|
+
The class works on any element with text content, including inside Zinc components. Colour variants mirror the standard Zinc palette: `zn-primary`, `zn-accent`, `zn-info`, `zn-success`, `zn-warning`, `zn-error`.
|
|
12
|
+
|
|
13
|
+
When `prefers-reduced-motion: reduce` is set, the animation is suppressed and the text falls back to a solid colour matching the chosen variant.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### Default
|
|
18
|
+
|
|
19
|
+
```html:preview
|
|
20
|
+
<span class="shimmer-text">Thinking…</span>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Colour Variants
|
|
24
|
+
|
|
25
|
+
```html:preview
|
|
26
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
27
|
+
<span class="shimmer-text">Default</span>
|
|
28
|
+
<span class="shimmer-text zn-primary">Primary</span>
|
|
29
|
+
<span class="shimmer-text zn-accent">Accent</span>
|
|
30
|
+
<span class="shimmer-text zn-info">Info</span>
|
|
31
|
+
<span class="shimmer-text zn-success">Success</span>
|
|
32
|
+
<span class="shimmer-text zn-warning">Warning</span>
|
|
33
|
+
<span class="shimmer-text zn-error">Error</span>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Headings and Larger Text
|
|
38
|
+
|
|
39
|
+
The shimmer scales with whatever `font-size` is on the element, so it can be applied to headings or inline within prose.
|
|
40
|
+
|
|
41
|
+
```html:preview
|
|
42
|
+
<h2 class="shimmer-text zn-primary" style="margin: 0;">Generating summary…</h2>
|
|
43
|
+
<p style="margin: 8px 0 0;">
|
|
44
|
+
Please wait while we <span class="shimmer-text zn-info">analyse your data</span>.
|
|
45
|
+
</p>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Inside Zinc Components
|
|
49
|
+
|
|
50
|
+
Because `shimmer-text` is a plain class, it composes with any Zinc component that renders slotted text.
|
|
51
|
+
|
|
52
|
+
```html:preview
|
|
53
|
+
<zn-chip class="shimmer-text zn-accent">Processing</zn-chip>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Tuning the Sweep
|
|
57
|
+
|
|
58
|
+
The effect mirrors the Claude CLI's character-level shimmer: a narrow, sharper highlight slides across text rather than a soft gradient wash. The class exposes these CSS custom properties — all sized in `ch` so they scale with the current font size.
|
|
59
|
+
|
|
60
|
+
| Variable | Default | Purpose |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `--shimmer-base` | `rgb(var(--zn-text))` | Resting text colour. |
|
|
63
|
+
| `--shimmer-highlight` | `rgb(var(--zn-text-heading))` | Colour of the spotlight. |
|
|
64
|
+
| `--shimmer-core` | `0.5ch` | Half-width of the solid-colour core of the spotlight. |
|
|
65
|
+
| `--shimmer-spread` | `1.5ch` | Half-width of the full highlight including its fade-out edges. |
|
|
66
|
+
| `--shimmer-duration` | `2s` | Time for one full sweep cycle. |
|
|
67
|
+
|
|
68
|
+
```html:preview
|
|
69
|
+
<div style="display: flex; flex-direction: column; gap: 6px;">
|
|
70
|
+
<span class="shimmer-text">Default sweep</span>
|
|
71
|
+
<span class="shimmer-text" style="--shimmer-duration: 1s;">Faster sweep</span>
|
|
72
|
+
<span class="shimmer-text" style="--shimmer-duration: 4s;">Slower, gentler sweep</span>
|
|
73
|
+
<span class="shimmer-text" style="--shimmer-core: 1ch; --shimmer-spread: 3ch;">Wider spotlight</span>
|
|
74
|
+
</div>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The Claude CLI scales its sweep with message length (50ms per character plus 10 characters of offscreen pause on each side). To match that feel in CSS, set `--shimmer-duration` to roughly `(messageCharacters + 20) × 50ms`.
|
|
78
|
+
|
|
79
|
+
### Reduced Motion
|
|
80
|
+
|
|
81
|
+
The animation is wrapped in `@media (prefers-reduced-motion: no-preference)`. Users who have requested reduced motion see the static colour variant instead — no configuration required.
|
package/package.json
CHANGED
package/scss/shared/index.scss
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
@keyframes shimmer-text-sweep {
|
|
3
|
+
0% {
|
|
4
|
+
background-position: 100% center;
|
|
5
|
+
}
|
|
6
|
+
100% {
|
|
7
|
+
background-position: 0% center;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
12
|
+
.shimmer-text {
|
|
13
|
+
--shimmer-base: rgb(var(--zn-text));
|
|
14
|
+
--shimmer-highlight: rgb(var(--zn-text-heading));
|
|
15
|
+
--shimmer-core: 0.5ch;
|
|
16
|
+
--shimmer-spread: 1.5ch;
|
|
17
|
+
--shimmer-duration: 1.5s;
|
|
18
|
+
|
|
19
|
+
display: inline-block;
|
|
20
|
+
width: fit-content;
|
|
21
|
+
max-width: 100%;
|
|
22
|
+
background-image:
|
|
23
|
+
linear-gradient(
|
|
24
|
+
90deg,
|
|
25
|
+
transparent calc(50% - var(--shimmer-spread)),
|
|
26
|
+
var(--shimmer-highlight) calc(50% - var(--shimmer-core)),
|
|
27
|
+
var(--shimmer-highlight) calc(50% + var(--shimmer-core)),
|
|
28
|
+
transparent calc(50% + var(--shimmer-spread))
|
|
29
|
+
),
|
|
30
|
+
linear-gradient(var(--shimmer-base), var(--shimmer-base));
|
|
31
|
+
background-size: 250% 100%, auto;
|
|
32
|
+
background-clip: text;
|
|
33
|
+
-webkit-background-clip: text;
|
|
34
|
+
-webkit-text-fill-color: transparent !important;
|
|
35
|
+
color: transparent !important;
|
|
36
|
+
animation: var(--shimmer-duration) linear infinite shimmer-text-sweep;
|
|
37
|
+
|
|
38
|
+
&.zn-error {
|
|
39
|
+
--shimmer-base: rgb(var(--zn-color-error));
|
|
40
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-color-error)) 55%, white);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&.zn-success {
|
|
44
|
+
--shimmer-base: rgb(var(--zn-color-success));
|
|
45
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-color-success)) 55%, white);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&.zn-info {
|
|
49
|
+
--shimmer-base: rgb(var(--zn-color-info));
|
|
50
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-color-info)) 55%, white);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&.zn-warning {
|
|
54
|
+
--shimmer-base: rgb(var(--zn-color-warning));
|
|
55
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-color-warning)) 55%, white);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&.zn-primary {
|
|
59
|
+
--shimmer-base: rgb(var(--zn-primary));
|
|
60
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-primary)) 55%, white);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&.zn-accent {
|
|
64
|
+
--shimmer-base: rgb(var(--zn-accent));
|
|
65
|
+
--shimmer-highlight: color-mix(in srgb, rgb(var(--zn-accent)) 55%, white);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (prefers-reduced-motion: reduce) {
|
|
71
|
+
.shimmer-text {
|
|
72
|
+
color: rgb(var(--zn-text));
|
|
73
|
+
|
|
74
|
+
&.zn-error {
|
|
75
|
+
color: rgb(var(--zn-color-error)) !important;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.zn-success {
|
|
79
|
+
color: rgb(var(--zn-color-success)) !important;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&.zn-info {
|
|
83
|
+
color: rgb(var(--zn-color-info)) !important;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
&.zn-warning {
|
|
87
|
+
color: rgb(var(--zn-color-warning)) !important;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&.zn-primary {
|
|
91
|
+
color: rgb(var(--zn-primary)) !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
&.zn-accent {
|
|
95
|
+
color: rgb(var(--zn-accent)) !important;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -269,6 +269,8 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
|
|
|
269
269
|
'panel--cosmic': this.cosmic,
|
|
270
270
|
'panel--shadow': this.shadow,
|
|
271
271
|
})}>
|
|
272
|
+
|
|
273
|
+
|
|
272
274
|
<div class="panel__inner">
|
|
273
275
|
${hasHeader ? html`
|
|
274
276
|
<zn-header class=${classMap({
|
|
@@ -297,6 +299,7 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
|
|
|
297
299
|
</label>` : ''}
|
|
298
300
|
|
|
299
301
|
<div part="toolbar" class="markdown-editor__toolbar">
|
|
302
|
+
${this.markdownHelperBtn()}
|
|
300
303
|
<zn-button-group class="markdown-editor__view-toggle" @click=${this.handleViewToggle}>
|
|
301
304
|
${VIEW_MODES.map(v => this.renderViewButton(v.mode, v.icon, v.label))}
|
|
302
305
|
</zn-button-group>
|
|
@@ -351,6 +354,21 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
|
|
|
351
354
|
`;
|
|
352
355
|
}
|
|
353
356
|
|
|
357
|
+
markdownHelperBtn() {
|
|
358
|
+
return html`
|
|
359
|
+
<a
|
|
360
|
+
class="markdown-editor__markdown-link"
|
|
361
|
+
href="https://www.markdownguide.org/cheat-sheet"
|
|
362
|
+
target="_blank"
|
|
363
|
+
rel="noopener noreferrer"
|
|
364
|
+
title="Markdown is supported"
|
|
365
|
+
>
|
|
366
|
+
<zn-icon src="markdown"></zn-icon>
|
|
367
|
+
<span>Markdown is supported</span>
|
|
368
|
+
</a>
|
|
369
|
+
`;
|
|
370
|
+
}
|
|
371
|
+
|
|
354
372
|
private renderViewButton(mode: ViewMode, icon: string, label: string) {
|
|
355
373
|
const isActive = this.viewMode === mode;
|
|
356
374
|
return html`
|
|
@@ -372,4 +390,7 @@ export default class ZnMarkdownEditor extends ZnPanel implements ZincFormControl
|
|
|
372
390
|
></zn-button>
|
|
373
391
|
`;
|
|
374
392
|
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
375
396
|
}
|
|
@@ -44,9 +44,32 @@
|
|
|
44
44
|
|
|
45
45
|
.markdown-editor__toolbar {
|
|
46
46
|
display: flex;
|
|
47
|
-
align-items:
|
|
47
|
+
align-items: end;
|
|
48
48
|
gap: var(--zn-spacing-small);
|
|
49
49
|
justify-content: flex-end;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.markdown-editor__markdown-link {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: var(--zn-spacing-x-small);
|
|
57
|
+
margin-right: auto;
|
|
58
|
+
font-size: var(--zn-input-label-font-size-small);
|
|
59
|
+
line-height: 1;
|
|
60
|
+
color: var(--zn-color-neutral-500, #656d76);
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
transition: color 0.15s ease;
|
|
64
|
+
padding-left: var(--zn-spacing-x-small);
|
|
65
|
+
|
|
66
|
+
&:hover {
|
|
67
|
+
color: var(--zn-color-primary-600, #0969da);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
span {
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
}
|
|
50
73
|
}
|
|
51
74
|
|
|
52
75
|
.markdown-editor__view-toggle {
|
|
@@ -37,6 +37,7 @@ export default class ZnTabs extends ZincElement {
|
|
|
37
37
|
@property({attribute: 'primary-caption', reflect: true}) primaryCaption = 'Navigation';
|
|
38
38
|
@property({attribute: 'secondary-caption', reflect: true}) secondaryCaption = 'Content';
|
|
39
39
|
@property({attribute: 'no-prefetch', type: Boolean, reflect: true}) noPrefetch = false;
|
|
40
|
+
@property({attribute: 'no-cache', type: Boolean, reflect: true}) noCache = false;
|
|
40
41
|
// session storage if not local
|
|
41
42
|
@property({attribute: 'local-storage', type: Boolean, reflect: true}) localStorage: boolean;
|
|
42
43
|
@property({attribute: 'store-key'}) storeKey: string;
|
|
@@ -72,7 +73,7 @@ export default class ZnTabs extends ZincElement {
|
|
|
72
73
|
this.masterId = this.storeKey || Math.floor(Math.random() * 1000000).toString();
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
this.preload = !this.noPrefetch;
|
|
76
|
+
this.preload = !this.noPrefetch && !this.noCache;
|
|
76
77
|
|
|
77
78
|
await this.updateComplete;
|
|
78
79
|
this._panel = this.shadowRoot?.querySelector('#content');
|
|
@@ -272,11 +273,14 @@ export default class ZnTabs extends ZincElement {
|
|
|
272
273
|
}
|
|
273
274
|
|
|
274
275
|
clickTab(target: HTMLElement, refresh: boolean) {
|
|
276
|
+
const tabUri = target.getAttribute('tab-uri');
|
|
277
|
+
const wasCached = !!tabUri && this._panels.has(this._uriToId(tabUri));
|
|
275
278
|
this.fetchUriTab(target);
|
|
276
279
|
|
|
277
280
|
if (target.hasAttribute('tab')) {
|
|
281
|
+
const forceRefresh = refresh || (this.noCache && wasCached);
|
|
278
282
|
setTimeout(() => {
|
|
279
|
-
this.setActiveTab(target.getAttribute('tab') || '', true,
|
|
283
|
+
this.setActiveTab(target.getAttribute('tab') || '', true, forceRefresh, this.getRefTab(target));
|
|
280
284
|
}, 10);
|
|
281
285
|
}
|
|
282
286
|
}
|