@timmo001/groundwork-ui 0.9.19 → 0.9.21
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/custom-elements.json +172 -13
- package/package.json +1 -1
- package/src/gw-button.ts +4 -0
- package/src/gw-card.ts +74 -10
- package/src/gw-menu-button.ts +46 -8
- package/src/gw-skeleton.ts +11 -2
- package/src/gw-spinner.ts +6 -1
- package/src/styles.ts +8 -0
package/custom-elements.json
CHANGED
|
@@ -3145,7 +3145,7 @@
|
|
|
3145
3145
|
"text": "readonly CSSStyleSheet[]"
|
|
3146
3146
|
},
|
|
3147
3147
|
"static": true,
|
|
3148
|
-
"default": "[ base, controlSizes(\"button\"), css` :host { --gw-button-tone: var(--gw-color-accent); display: inline-block; } :host([variant=\"neutral\"]) { --gw-button-tone: var(--gw-color-text-secondary); --gw-button-ink: var(--gw-color-text-strong); } :host([variant=\"info\"]) { --gw-button-tone: var(--gw-color-info); } :host([variant=\"success\"]) { --gw-button-tone: var(--gw-color-success); } :host([variant=\"warning\"]) { --gw-button-tone: var(--gw-color-warning); } :host([variant=\"danger\"]) { --gw-button-tone: var(--gw-color-danger); } .button { --gw-size: var(--gw-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.125rem * var(--gw-scale)); position: relative; display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; justify-content: center; min-height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); line-height: 1; color: var(--gw-button-ink, var(--gw-button-tone)); text-decoration: none; text-transform: var(--gw-label-transform); letter-spacing: var(--gw-label-tracking); white-space: nowrap; cursor: pointer; background: transparent; border: max(var(--gw-border-width), 1px) solid transparent; border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing), border-color var(--gw-motion-fast) var(--gw-motion-easing), color var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-fast) var(--gw-motion-easing); } /* A slight press, so a click feels physical */ .button:active { scale: 0.97; } .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } .button:active { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } /* A solid fill, with text that flips between black and white by the fill's lightness so it reads on every theme's colors */ :host([appearance=\"accent\"]) .button { color: oklch( from var(--gw-button-tone) clamp(0, (0.66 - l) * 1000, 1) 0 0 ); background: var(--gw-button-tone); } :host([appearance=\"accent\"]) .button:hover { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.2) c h ); } :host([appearance=\"accent\"]) .button:active { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.35) c h ); } /* The tint lowers the tone's contrast, so the text leans toward the strongest text color to stay readable on it */ :host([appearance=\"filled\"]) .button { color: var( --gw-button-ink, color-mix( in oklch, var(--gw-button-tone) 70%, var(--gw-color-text-strong) ) ); background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } :host([appearance=\"filled\"]) .button:active { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } :host([appearance=\"outlined\"]) .button { border-color: var(--gw-button-tone); } /* A slotted link carries the text; the pill draws its focus ring */ ::slotted(a) { color: inherit; text-decoration: none; outline: none; } .button.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :host([appearance=\"plain\"]) .button.current, :host([appearance=\"outlined\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } .button:disabled, .button[aria-disabled=\"true\"] { color: var(--gw-color-text-disabled); pointer-events: none; cursor: not-allowed; background: var(--gw-color-fill-neutral-disabled); border-color: transparent; } :host([appearance=\"plain\"]) .button:disabled, :host([appearance=\"plain\"]) .button[aria-disabled=\"true\"] { background: transparent; } ::slotted([slot=\"icon\"]), .icon { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); fill: currentColor; } /* The icon and the ring share one cell and crossfade */ .lead { display: grid; place-items: center; } /* The icon slot is display: contents, so its slotted icon is a grid item of its own and needs the shared cell too */ .lead > *, ::slotted([slot=\"icon\"]) { grid-area: 1 / 1; } /* A color dot in the icon's place */ .swatch { width: calc(var(--gw-icon-size) * 0.6); height: calc(var(--gw-icon-size) * 0.6); border: 1px solid color-mix(in oklab, var(--gw-color-text-primary) 20%, transparent); border-radius: 50%; } .lead > *, .text, .ring { transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1); } /* Sized to the icon's drawn area, which is inset in its 24px box */ .ring { width: calc(var(--gw-icon-size) * 0.8); height: calc(var(--gw-icon-size) * 0.8); border: calc(2px * var(--gw-scale)) solid color-mix(in oklch, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; opacity: 0; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Without an icon, the ring sits over the hidden text */ .ring.over { position: absolute; inset: 0; margin: auto; } :host([loading]) .button { cursor: progress; } :host([loading]) .lead > .ring, :host([loading]) .button:not(.has-icon) > .ring.over { opacity: 1; } :host([loading]) .lead > :not(.ring), :host([loading]) .button:not(.has-icon) > .text { opacity: 0; } .button:not(.has-icon) > .lead { display: none; } /* Icon only, to save space: square, with the text kept for assistive technology. auto does this on narrow screens */ :host([labels=\"hide\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"hide\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"auto\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } `, ]",
|
|
3148
|
+
"default": "[ base, controlSizes(\"button\"), css` :host { --gw-button-tone: var(--gw-color-accent); display: inline-block; } :host([variant=\"neutral\"]) { --gw-button-tone: var(--gw-color-text-secondary); --gw-button-ink: var(--gw-color-text-strong); } :host([variant=\"info\"]) { --gw-button-tone: var(--gw-color-info); } :host([variant=\"success\"]) { --gw-button-tone: var(--gw-color-success); } :host([variant=\"warning\"]) { --gw-button-tone: var(--gw-color-warning); } :host([variant=\"danger\"]) { --gw-button-tone: var(--gw-color-danger); } .button { --gw-size: var(--gw-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.125rem * var(--gw-scale)); position: relative; display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; justify-content: center; min-height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); line-height: 1; color: var(--gw-button-ink, var(--gw-button-tone)); text-decoration: none; text-transform: var(--gw-label-transform); letter-spacing: var(--gw-label-tracking); white-space: nowrap; cursor: pointer; background: transparent; border: max(var(--gw-border-width), 1px) solid transparent; border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing), border-color var(--gw-motion-fast) var(--gw-motion-easing), color var(--gw-motion-fast) var(--gw-motion-easing), scale var(--gw-motion-fast) var(--gw-motion-easing); } /* A slight press, so a click feels physical */ .button:active { scale: 0.97; } .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } .button:active { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } /* A solid fill, with text that flips between black and white by the fill's lightness so it reads on every theme's colors */ :host([appearance=\"accent\"]) .button { color: oklch( from var(--gw-button-tone) clamp(0, (0.66 - l) * 1000, 1) 0 0 ); background: var(--gw-button-tone); } :host([appearance=\"accent\"]) .button:hover { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.2) c h ); } :host([appearance=\"accent\"]) .button:active { background: oklch( from var(--gw-button-tone) calc(l + (0.6 - l) * 0.35) c h ); } /* The tint lowers the tone's contrast, so the text leans toward the strongest text color to stay readable on it */ :host([appearance=\"filled\"]) .button { color: var( --gw-button-ink, color-mix( in oklch, var(--gw-button-tone) 70%, var(--gw-color-text-strong) ) ); background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button:hover { background: color-mix(in oklch, var(--gw-button-tone) 24%, transparent); } :host([appearance=\"filled\"]) .button:active { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } :host([appearance=\"outlined\"]) .button { border-color: var(--gw-button-tone); } /* A slotted link carries the text; the pill draws its focus ring */ ::slotted(a) { color: inherit; text-decoration: none; outline: none; } .button.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } :host([appearance=\"plain\"]) .button.current, :host([appearance=\"outlined\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 16%, transparent); } :host([appearance=\"filled\"]) .button.current { background: color-mix(in oklch, var(--gw-button-tone) 32%, transparent); } .button:disabled, .button[aria-disabled=\"true\"] { color: var(--gw-color-text-disabled); pointer-events: none; cursor: not-allowed; background: var(--gw-color-fill-neutral-disabled); border-color: transparent; } :host([appearance=\"plain\"]) .button:disabled, :host([appearance=\"plain\"]) .button[aria-disabled=\"true\"] { background: transparent; } ::slotted([slot=\"icon\"]), .icon { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); fill: currentColor; } /* The icon and the ring share one cell and crossfade */ .lead { display: grid; place-items: center; } /* The icon slot is display: contents, so its slotted icon is a grid item of its own and needs the shared cell too */ .lead > *, ::slotted([slot=\"icon\"]) { grid-area: 1 / 1; } /* A color dot in the icon's place */ .swatch { width: calc(var(--gw-icon-size) * 0.6); height: calc(var(--gw-icon-size) * 0.6); border: 1px solid color-mix(in oklab, var(--gw-color-text-primary) 20%, transparent); border-radius: 50%; } .lead > *, .text, .ring { transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1); } /* Sized to the icon's drawn area, which is inset in its 24px box */ .ring { width: calc(var(--gw-icon-size) * 0.8); height: calc(var(--gw-icon-size) * 0.8); border: calc(2px * var(--gw-scale)) solid color-mix(in oklch, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; opacity: 0; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Without an icon, the ring sits over the hidden text */ .ring.over { position: absolute; inset: 0; margin: auto; } :host([loading]) .button { cursor: progress; } /* The icon stays for --gw-motion-delay, so a quick action never flashes the ring, then they crossfade; ending is immediate */ :host([loading]) .lead > .ring, :host([loading]) .button:not(.has-icon) > .ring.over { opacity: 1; transition-delay: var(--gw-motion-delay, 500ms); } :host([loading]) .lead > :not(.ring), :host([loading]) .button:not(.has-icon) > .text { opacity: 0; transition-delay: var(--gw-motion-delay, 500ms); } .button:not(.has-icon) > .lead { display: none; } /* Icon only, to save space: square, with the text kept for assistive technology. auto does this on narrow screens */ :host([labels=\"hide\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"hide\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .button.has-icon { min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"auto\"]) .has-icon .text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } `, ]",
|
|
3149
3149
|
"inheritedFrom": {
|
|
3150
3150
|
"name": "GwElement",
|
|
3151
3151
|
"module": "src/element.ts"
|
|
@@ -3722,9 +3722,17 @@
|
|
|
3722
3722
|
"description": "The heading.",
|
|
3723
3723
|
"name": "heading"
|
|
3724
3724
|
},
|
|
3725
|
+
{
|
|
3726
|
+
"description": "The line under the heading.",
|
|
3727
|
+
"name": "description"
|
|
3728
|
+
},
|
|
3725
3729
|
{
|
|
3726
3730
|
"description": "The content.",
|
|
3727
3731
|
"name": "body"
|
|
3732
|
+
},
|
|
3733
|
+
{
|
|
3734
|
+
"description": "The area under the divider.",
|
|
3735
|
+
"name": "footer"
|
|
3728
3736
|
}
|
|
3729
3737
|
],
|
|
3730
3738
|
"slots": [
|
|
@@ -3739,6 +3747,10 @@
|
|
|
3739
3747
|
{
|
|
3740
3748
|
"description": "Buttons beside the heading.",
|
|
3741
3749
|
"name": "actions"
|
|
3750
|
+
},
|
|
3751
|
+
{
|
|
3752
|
+
"description": "Content under a divider at the bottom, such as a control for the whole card.",
|
|
3753
|
+
"name": "footer"
|
|
3742
3754
|
}
|
|
3743
3755
|
],
|
|
3744
3756
|
"members": [
|
|
@@ -3749,7 +3761,7 @@
|
|
|
3749
3761
|
"text": "Properties"
|
|
3750
3762
|
},
|
|
3751
3763
|
"static": true,
|
|
3752
|
-
"default": "{ heading: {}, href: {}, flush: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, lines: { type: \"number\" }, }",
|
|
3764
|
+
"default": "{ heading: {}, description: {}, level: { type: \"number\" }, href: {}, flush: { type: \"boolean\", reflect: true }, loading: { type: \"boolean\", reflect: true }, lines: { type: \"number\" }, }",
|
|
3753
3765
|
"inheritedFrom": {
|
|
3754
3766
|
"name": "GwElement",
|
|
3755
3767
|
"module": "src/element.ts"
|
|
@@ -3762,7 +3774,7 @@
|
|
|
3762
3774
|
"text": "readonly CSSStyleSheet[]"
|
|
3763
3775
|
},
|
|
3764
3776
|
"static": true,
|
|
3765
|
-
"default": "[ base, css` :host { display: block; } .card { display: flex; flex-direction: column; gap: var(--gw-space-3); height: 100%; padding: var(--gw-space-4); color: inherit; text-decoration: none; background: var(--gw-color-surface-raised); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-panel); box-shadow: var(--gw-shadow-s); } :host([flush]) .card { padding: 0; overflow: hidden; } a.card { transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } a.card:hover, .card.linked:hover { border-color: var(--gw-color-border-accent); } /* A card following a link in its heading slot */ .card.linked { cursor: pointer; transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } .card.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } ::slotted([slot=\"heading\"]) { font: inherit; color: inherit; } ::slotted(a[slot=\"heading\"]) { text-decoration: none; outline: none; } header { display: flex; gap: var(--gw-space-2); align-items: center; }
|
|
3777
|
+
"default": "[ base, css` :host { display: block; } .card { display: flex; flex-direction: column; gap: var(--gw-space-3); height: 100%; padding: var(--gw-space-4); color: inherit; text-decoration: none; background: var(--gw-color-surface-raised); border: var(--gw-border-width) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-panel); box-shadow: var(--gw-shadow-s); } :host([flush]) .card { padding: 0; overflow: hidden; } a.card { transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } a.card:hover, .card.linked:hover { border-color: var(--gw-color-border-accent); } /* A card following a link in its heading slot */ .card.linked { cursor: pointer; transition: border-color var(--gw-motion-fast) var(--gw-motion-easing); } .card.focused { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } ::slotted([slot=\"heading\"]) { font: inherit; color: inherit; } ::slotted(a[slot=\"heading\"]) { text-decoration: none; outline: none; } header { display: flex; gap: var(--gw-space-2); align-items: center; } .titles { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } .title { margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } /* The divider runs to the card's edges */ footer:not([hidden]) { display: flex; flex-direction: column; gap: inherit; margin-inline: calc(-1 * var(--gw-space-4)); padding: var(--gw-space-3) var(--gw-space-4) 0; border-top: var(--gw-border-width) solid var(--gw-color-border-normal); } :host([flush]) footer:not([hidden]) { margin-inline: 0; padding-bottom: var(--gw-space-3); } .body:not([hidden]) { display: flex; flex-direction: column; gap: inherit; } `, ]",
|
|
3766
3778
|
"inheritedFrom": {
|
|
3767
3779
|
"name": "GwElement",
|
|
3768
3780
|
"module": "src/element.ts"
|
|
@@ -3776,6 +3788,23 @@
|
|
|
3776
3788
|
},
|
|
3777
3789
|
"default": "\"\""
|
|
3778
3790
|
},
|
|
3791
|
+
{
|
|
3792
|
+
"kind": "field",
|
|
3793
|
+
"name": "description",
|
|
3794
|
+
"type": {
|
|
3795
|
+
"text": "string"
|
|
3796
|
+
},
|
|
3797
|
+
"default": "\"\""
|
|
3798
|
+
},
|
|
3799
|
+
{
|
|
3800
|
+
"kind": "field",
|
|
3801
|
+
"name": "level",
|
|
3802
|
+
"type": {
|
|
3803
|
+
"text": "number"
|
|
3804
|
+
},
|
|
3805
|
+
"description": "The heading's level for assistive tech.",
|
|
3806
|
+
"default": "2"
|
|
3807
|
+
},
|
|
3779
3808
|
{
|
|
3780
3809
|
"kind": "field",
|
|
3781
3810
|
"name": "href",
|
|
@@ -3827,6 +3856,18 @@
|
|
|
3827
3856
|
"privacy": "private",
|
|
3828
3857
|
"readonly": true
|
|
3829
3858
|
},
|
|
3859
|
+
{
|
|
3860
|
+
"kind": "field",
|
|
3861
|
+
"name": "#heading",
|
|
3862
|
+
"privacy": "private",
|
|
3863
|
+
"readonly": true
|
|
3864
|
+
},
|
|
3865
|
+
{
|
|
3866
|
+
"kind": "field",
|
|
3867
|
+
"name": "#description",
|
|
3868
|
+
"privacy": "private",
|
|
3869
|
+
"readonly": true
|
|
3870
|
+
},
|
|
3830
3871
|
{
|
|
3831
3872
|
"kind": "field",
|
|
3832
3873
|
"name": "#header",
|
|
@@ -3836,6 +3877,21 @@
|
|
|
3836
3877
|
},
|
|
3837
3878
|
"readonly": true
|
|
3838
3879
|
},
|
|
3880
|
+
{
|
|
3881
|
+
"kind": "field",
|
|
3882
|
+
"name": "#footerSlot",
|
|
3883
|
+
"privacy": "private",
|
|
3884
|
+
"readonly": true
|
|
3885
|
+
},
|
|
3886
|
+
{
|
|
3887
|
+
"kind": "field",
|
|
3888
|
+
"name": "#footer",
|
|
3889
|
+
"privacy": "private",
|
|
3890
|
+
"type": {
|
|
3891
|
+
"text": "HTMLElement"
|
|
3892
|
+
},
|
|
3893
|
+
"readonly": true
|
|
3894
|
+
},
|
|
3839
3895
|
{
|
|
3840
3896
|
"kind": "field",
|
|
3841
3897
|
"name": "#skeleton",
|
|
@@ -4208,6 +4264,20 @@
|
|
|
4208
4264
|
"description": "The card's heading.",
|
|
4209
4265
|
"name": "heading"
|
|
4210
4266
|
},
|
|
4267
|
+
{
|
|
4268
|
+
"type": {
|
|
4269
|
+
"text": "string"
|
|
4270
|
+
},
|
|
4271
|
+
"description": "A line under the heading.",
|
|
4272
|
+
"name": "description"
|
|
4273
|
+
},
|
|
4274
|
+
{
|
|
4275
|
+
"type": {
|
|
4276
|
+
"text": "number"
|
|
4277
|
+
},
|
|
4278
|
+
"description": "The heading's level for assistive tech, 2 by default. Use 3 for a card under a section heading.",
|
|
4279
|
+
"name": "level"
|
|
4280
|
+
},
|
|
4211
4281
|
{
|
|
4212
4282
|
"type": {
|
|
4213
4283
|
"text": "string"
|
|
@@ -10441,7 +10511,7 @@
|
|
|
10441
10511
|
"text": "readonly CSSStyleSheet[]"
|
|
10442
10512
|
},
|
|
10443
10513
|
"static": true,
|
|
10444
|
-
"default": "[ base, controlSizes(\"menu-button\"), css` :host { position: relative; display: inline-block; anchor-scope: --gw-menu; } .trigger { --gw-size: var(--gw-menu-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.25rem * var(--gw-scale)); display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; box-sizing: border-box; height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); cursor: pointer; background: var(--gw-color-fill-neutral-resting); border: var(--gw-border-width) solid var(--gw-border-color); border-radius: var(--gw-radius-control); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); anchor-name: --gw-menu; } .trigger:hover { background: var(--gw-color-fill-neutral-hovered); } /* While a chosen action runs, a ring takes the icon's place
|
|
10514
|
+
"default": "[ base, controlSizes(\"menu-button\"), css` :host { position: relative; display: inline-block; anchor-scope: --gw-menu; } .trigger { --gw-size: var(--gw-menu-button-scale, 1); --gw-scale: calc(var(--gw-size) * var(--gw-control-scale, 1)); --gw-icon-size: calc(1.25rem * var(--gw-scale)); display: inline-flex; gap: calc(var(--gw-space-2) * var(--gw-size)); align-items: center; box-sizing: border-box; height: calc(2.5rem * var(--gw-scale)); padding: 0 calc(var(--gw-space-4) * var(--gw-size)); font: inherit; font-size: calc(var(--gw-font-size-s) * var(--gw-size)); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); cursor: pointer; background: var(--gw-color-fill-neutral-resting); border: var(--gw-border-width) solid var(--gw-border-color); border-radius: var(--gw-radius-control); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); anchor-name: --gw-menu; } .trigger:hover { background: var(--gw-color-fill-neutral-hovered); } /* While a chosen action runs, a ring takes the icon's place: they share one cell and crossfade after --gw-motion-delay, so a quick action never flashes it and nothing moves. Without an icon, the ring sits over the hidden text. */ .lead { display: grid; flex: none; place-items: center; } .trigger:not(.has-icon) > .lead { display: contents; } .lead > * { grid-area: 1 / 1; } .lead > *, .label { transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1); } .ring { width: calc(var(--gw-icon-size) * 0.8); height: calc(var(--gw-icon-size) * 0.8); border: calc(2px * var(--gw-scale)) solid color-mix(in oklch, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; opacity: 0; animation: spin 0.9s linear infinite; } .trigger:not(.has-icon) .ring { position: absolute; inset: 0; margin: auto; } .trigger { position: relative; } @keyframes spin { to { transform: rotate(360deg); } } :host([loading]) .trigger { cursor: progress; } :host([loading]) .ring { opacity: 1; transition-delay: var(--gw-motion-delay, 500ms); } :host([loading]) .lead > .icon, :host([loading]) .trigger:not(.has-icon) > .label { opacity: 0; transition-delay: var(--gw-motion-delay, 500ms); } /* The other appearances draw the trigger as gw-button draws a neutral one, a pill, so it sits in a row of buttons without a seam */ :host([appearance=\"accent\"]) .trigger, :host([appearance=\"outlined\"]) .trigger, :host([appearance=\"plain\"]) .trigger { background: transparent; border: max(var(--gw-border-width), 1px) solid transparent; border-radius: var(--gw-radius-pill); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing), border-color var(--gw-motion-fast) var(--gw-motion-easing), color var(--gw-motion-fast) var(--gw-motion-easing); } :host([appearance=\"outlined\"]) .trigger { border-color: var(--gw-color-text-secondary); } :host([appearance=\"outlined\"]) .trigger:hover, :host([appearance=\"plain\"]) .trigger:hover { background: color-mix( in oklch, var(--gw-color-text-secondary) 16%, transparent ); } /* A solid fill, with text that flips between black and white by the fill's lightness, as gw-button's accent does */ :host([appearance=\"accent\"]) .trigger { color: oklch( from var(--gw-color-accent) clamp(0, (0.66 - l) * 1000, 1) 0 0 ); background: var(--gw-color-accent); } :host([appearance=\"accent\"]) .trigger:hover { background: oklch( from var(--gw-color-accent) calc(l + (0.6 - l) * 0.2) c h ); } :host([active]:not([appearance=\"accent\"])) .trigger { color: var(--gw-color-accent); border-color: var(--gw-color-accent); } /* Icon only, square like an icon-only gw-button, such as a ⋮ for more actions, with the text kept for assistive technology */ :host([labels=\"hide\"]) .trigger.has-icon { justify-content: center; min-width: calc(2.5rem * var(--gw-scale)); padding: 0; } :host([labels=\"hide\"]) .has-icon .chevron { display: none; } :host([labels=\"hide\"]) .has-icon .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @media (width < 40rem) { :host([labels=\"auto\"]) .trigger.has-icon { padding: 0 calc(var(--gw-space-2) * var(--gw-size)); } :host([labels=\"auto\"]) .has-icon .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } } .chevron { width: 0.4rem; height: 0.4rem; margin-inline-start: var(--gw-space-1); border-inline-end: 1.5px solid currentColor; border-block-end: 1.5px solid currentColor; rotate: 45deg; translate: 0 -0.125rem; transition: rotate var(--gw-motion-normal) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing); } .trigger[aria-expanded=\"true\"] .chevron { rotate: 225deg; translate: 0 0.125rem; } /* Below the button down to the window's edge, or above it when there's more room there, growing to its content and scrolling past that */ [popover] { position-anchor: --gw-menu; position-try-fallbacks: flip-inline; /* The area is the room on that side, so 100% is all of it */ position-area: block-end span-inline-end; inset: auto; box-sizing: border-box; min-width: 15rem; max-width: calc(100vw - 2 * var(--gw-space-2)); max-height: min(24rem, calc(100% - 2 * var(--gw-space-2))); padding: 0; margin: var(--gw-space-1) 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); border-radius: var(--gw-radius-l); box-shadow: var(--gw-shadow-m); opacity: 0; translate: 0 -0.25rem; transition: opacity var(--gw-motion-fast) var(--gw-motion-easing), translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; flex-direction: column; opacity: 1; translate: 0 0; } @starting-style { [popover]:popover-open { opacity: 0; translate: 0 -0.25rem; } } /* The items scroll; the actions stay put below them, so \"Add\" is always in reach rather than at the end of a long list */ .items, .actions { display: flex; flex-direction: column; gap: var(--gw-space-0-5); padding: var(--gw-space-1); } .items { flex: 1; min-height: 0; overflow-y: auto; } .actions { border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .empty { padding: var(--gw-space-3); margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } .item { display: flex; gap: var(--gw-space-3); align-items: center; box-sizing: border-box; min-height: 2.5rem; padding: var(--gw-space-2) var(--gw-space-3); font: inherit; font-size: var(--gw-font-size-s); color: var(--gw-color-text-primary); text-align: start; cursor: pointer; background: transparent; border: 0; border-radius: var(--gw-radius-m); transition: background-color var(--gw-motion-fast) var(--gw-motion-easing); --gw-icon-size: 1.125rem; } .item:hover, .item:focus-visible { background: var(--gw-color-fill-neutral-hovered); outline: none; } .item:focus-visible { box-shadow: inset 0 0 0 var(--gw-focus-width) var(--gw-color-focus); } .item:disabled { color: var(--gw-color-text-disabled); cursor: not-allowed; background: transparent; } .action { font-weight: var(--gw-font-weight-strong); color: var(--gw-color-accent); } /* Holds the tick's place on every choice, so the labels line up */ .tick { flex: none; width: var(--gw-icon-size); height: var(--gw-icon-size); color: var(--gw-color-accent); } .item[aria-checked=\"false\"] .tick { visibility: hidden; } .item[role=\"menuitemcheckbox\"] .box { flex: none; color: var(--gw-color-text-secondary); } .item[role=\"menuitemcheckbox\"][aria-checked=\"true\"] .box { color: var(--gw-color-accent); } .group { display: flex; flex-direction: column; gap: var(--gw-space-0-5); } .group:not(:first-child) { padding-block-start: var(--gw-space-1); margin-block-start: var(--gw-space-0-5); border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .group-heading { padding: var(--gw-space-1) var(--gw-space-3) 0; margin: 0; font-size: var(--gw-font-size-xs); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-secondary); } .separator { flex: none; height: 0; margin: var(--gw-space-0-5) calc(-1 * var(--gw-space-1)); border: 0; border-block-start: max(var(--gw-border-width), 1px) solid var(--gw-color-border-normal); } .text { display: flex; flex-direction: column; gap: var(--gw-space-0-5); } .description { font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); } `, ]",
|
|
10445
10515
|
"inheritedFrom": {
|
|
10446
10516
|
"name": "GwElement",
|
|
10447
10517
|
"module": "src/element.ts"
|
|
@@ -11121,6 +11191,12 @@
|
|
|
11121
11191
|
"kind": "class",
|
|
11122
11192
|
"description": "A labeled bar for a value in a range, such as disk use. Past `high` it\nturns warning, and past `critical` danger, unless a `tone` is set. While\n`loading`, the track pulses and a skeleton stands in for the value.",
|
|
11123
11193
|
"name": "GwMeter",
|
|
11194
|
+
"cssProperties": [
|
|
11195
|
+
{
|
|
11196
|
+
"description": "The bar's height. Defaults to `0.5rem`.",
|
|
11197
|
+
"name": "--gw-meter-height"
|
|
11198
|
+
}
|
|
11199
|
+
],
|
|
11124
11200
|
"cssParts": [
|
|
11125
11201
|
{
|
|
11126
11202
|
"description": "The row with the label and value.",
|
|
@@ -11137,6 +11213,10 @@
|
|
|
11137
11213
|
{
|
|
11138
11214
|
"description": "The filled part of the bar.",
|
|
11139
11215
|
"name": "fill"
|
|
11216
|
+
},
|
|
11217
|
+
{
|
|
11218
|
+
"description": "The line under the bar.",
|
|
11219
|
+
"name": "caption"
|
|
11140
11220
|
}
|
|
11141
11221
|
],
|
|
11142
11222
|
"members": [
|
|
@@ -11147,7 +11227,7 @@
|
|
|
11147
11227
|
"text": "Properties"
|
|
11148
11228
|
},
|
|
11149
11229
|
"static": true,
|
|
11150
|
-
"default": "{ label: {}, value: { type: \"number\" }, min: { type: \"number\" }, max: { type: \"number\" }, high: { type: \"number\" }, critical: { type: \"number\" }, display: {}, tone: { reflect: true }, loading: { type: \"boolean\", reflect: true }, }",
|
|
11230
|
+
"default": "{ label: {}, value: { type: \"number\" }, min: { type: \"number\" }, max: { type: \"number\" }, high: { type: \"number\" }, critical: { type: \"number\" }, inverted: { type: \"boolean\" }, display: {}, caption: {}, tone: { reflect: true }, loading: { type: \"boolean\", reflect: true }, }",
|
|
11151
11231
|
"inheritedFrom": {
|
|
11152
11232
|
"name": "GwElement",
|
|
11153
11233
|
"module": "src/element.ts"
|
|
@@ -11160,7 +11240,7 @@
|
|
|
11160
11240
|
"text": "readonly CSSStyleSheet[]"
|
|
11161
11241
|
},
|
|
11162
11242
|
"static": true,
|
|
11163
|
-
"default": "[ base, css` :host { display: block; } .top { display: flex; gap: var(--gw-space-2); justify-content: space-between; margin-block-end: var(--gw-space-1); font-size: var(--gw-font-size-s); } .value { font-variant-numeric: var(--gw-font-figures); color: var(--gw-color-text-strong); } .track { height: 0.5rem; overflow: hidden; background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-quiet); border-radius: var(--gw-radius-pill); } .fill { height: 100%; background: var(--gw-meter-color, var(--gw-color-fill-accent-resting)); border-radius: inherit; transition: width var(--gw-motion-slow) var(--gw-motion-easing); } .bar { min-height: 0; height: 0.5rem; --gw-skeleton-radius: var(--gw-radius-pill); } `, ]",
|
|
11243
|
+
"default": "[ base, css` :host { display: block; } .top { display: flex; gap: var(--gw-space-2); justify-content: space-between; margin-block-end: var(--gw-space-1); font-size: var(--gw-font-size-s); } .value { font-variant-numeric: var(--gw-font-figures); color: var(--gw-color-text-strong); } .track { height: var(--gw-meter-height, 0.5rem); overflow: hidden; background: var(--gw-color-surface-lower); border: var(--gw-border-width) solid var(--gw-color-border-quiet); border-radius: var(--gw-radius-pill); } .fill { height: 100%; background: var(--gw-meter-color, var(--gw-color-fill-accent-resting)); border-radius: inherit; transition: width var(--gw-motion-slow) var(--gw-motion-easing); } .bar { min-height: 0; height: var(--gw-meter-height, 0.5rem); --gw-skeleton-radius: var(--gw-radius-pill); } .caption { display: block; margin-block-start: var(--gw-space-1); overflow: hidden; font-size: var(--gw-font-size-xs); color: var(--gw-color-text-secondary); text-overflow: ellipsis; white-space: nowrap; } .caption[hidden] { display: none; } `, ]",
|
|
11164
11244
|
"inheritedFrom": {
|
|
11165
11245
|
"name": "GwElement",
|
|
11166
11246
|
"module": "src/element.ts"
|
|
@@ -11178,7 +11258,7 @@
|
|
|
11178
11258
|
"kind": "field",
|
|
11179
11259
|
"name": "value",
|
|
11180
11260
|
"type": {
|
|
11181
|
-
"text": "number"
|
|
11261
|
+
"text": "number | undefined"
|
|
11182
11262
|
},
|
|
11183
11263
|
"default": "0"
|
|
11184
11264
|
},
|
|
@@ -11214,6 +11294,14 @@
|
|
|
11214
11294
|
},
|
|
11215
11295
|
"default": "undefined"
|
|
11216
11296
|
},
|
|
11297
|
+
{
|
|
11298
|
+
"kind": "field",
|
|
11299
|
+
"name": "inverted",
|
|
11300
|
+
"type": {
|
|
11301
|
+
"text": "boolean"
|
|
11302
|
+
},
|
|
11303
|
+
"default": "false"
|
|
11304
|
+
},
|
|
11217
11305
|
{
|
|
11218
11306
|
"kind": "field",
|
|
11219
11307
|
"name": "display",
|
|
@@ -11222,6 +11310,14 @@
|
|
|
11222
11310
|
},
|
|
11223
11311
|
"default": "\"\""
|
|
11224
11312
|
},
|
|
11313
|
+
{
|
|
11314
|
+
"kind": "field",
|
|
11315
|
+
"name": "caption",
|
|
11316
|
+
"type": {
|
|
11317
|
+
"text": "string"
|
|
11318
|
+
},
|
|
11319
|
+
"default": "\"\""
|
|
11320
|
+
},
|
|
11225
11321
|
{
|
|
11226
11322
|
"kind": "field",
|
|
11227
11323
|
"name": "tone",
|
|
@@ -11286,6 +11382,12 @@
|
|
|
11286
11382
|
"privacy": "private",
|
|
11287
11383
|
"readonly": true
|
|
11288
11384
|
},
|
|
11385
|
+
{
|
|
11386
|
+
"kind": "field",
|
|
11387
|
+
"name": "#caption",
|
|
11388
|
+
"privacy": "private",
|
|
11389
|
+
"readonly": true
|
|
11390
|
+
},
|
|
11289
11391
|
{
|
|
11290
11392
|
"kind": "field",
|
|
11291
11393
|
"name": "#rendered",
|
|
@@ -11295,6 +11397,25 @@
|
|
|
11295
11397
|
},
|
|
11296
11398
|
"default": "false"
|
|
11297
11399
|
},
|
|
11400
|
+
{
|
|
11401
|
+
"kind": "field",
|
|
11402
|
+
"name": "#known",
|
|
11403
|
+
"privacy": "private",
|
|
11404
|
+
"readonly": true
|
|
11405
|
+
},
|
|
11406
|
+
{
|
|
11407
|
+
"kind": "method",
|
|
11408
|
+
"name": "#worse",
|
|
11409
|
+
"privacy": "private",
|
|
11410
|
+
"parameters": [
|
|
11411
|
+
{
|
|
11412
|
+
"name": "limit",
|
|
11413
|
+
"type": {
|
|
11414
|
+
"text": "number | undefined"
|
|
11415
|
+
}
|
|
11416
|
+
}
|
|
11417
|
+
]
|
|
11418
|
+
},
|
|
11298
11419
|
{
|
|
11299
11420
|
"kind": "field",
|
|
11300
11421
|
"name": "#color",
|
|
@@ -11625,7 +11746,7 @@
|
|
|
11625
11746
|
"type": {
|
|
11626
11747
|
"text": "number"
|
|
11627
11748
|
},
|
|
11628
|
-
"description": "The value. Defaults to 0.",
|
|
11749
|
+
"description": "The value. Defaults to 0. Leave it unset or `NaN` for an unknown value, shown as `-` with an empty bar.",
|
|
11629
11750
|
"name": "value"
|
|
11630
11751
|
},
|
|
11631
11752
|
{
|
|
@@ -11656,6 +11777,13 @@
|
|
|
11656
11777
|
"description": "Past this it turns danger.",
|
|
11657
11778
|
"name": "critical"
|
|
11658
11779
|
},
|
|
11780
|
+
{
|
|
11781
|
+
"type": {
|
|
11782
|
+
"text": "boolean"
|
|
11783
|
+
},
|
|
11784
|
+
"description": "Lower is worse, such as for a battery: at or below `high` it turns warning, and at or below `critical` danger.",
|
|
11785
|
+
"name": "inverted"
|
|
11786
|
+
},
|
|
11659
11787
|
{
|
|
11660
11788
|
"type": {
|
|
11661
11789
|
"text": "string"
|
|
@@ -11663,6 +11791,13 @@
|
|
|
11663
11791
|
"description": "The value as shown, such as `42 GB`. Defaults to a percentage.",
|
|
11664
11792
|
"name": "display"
|
|
11665
11793
|
},
|
|
11794
|
+
{
|
|
11795
|
+
"type": {
|
|
11796
|
+
"text": "string"
|
|
11797
|
+
},
|
|
11798
|
+
"description": "A quiet line under the bar.",
|
|
11799
|
+
"name": "caption"
|
|
11800
|
+
},
|
|
11666
11801
|
{
|
|
11667
11802
|
"type": {
|
|
11668
11803
|
"text": "\"neutral\"|\"accent\"|\"info\"|\"success\"|\"warning\"|\"danger\"|\"important\""
|
|
@@ -17216,7 +17351,7 @@
|
|
|
17216
17351
|
"declarations": [
|
|
17217
17352
|
{
|
|
17218
17353
|
"kind": "class",
|
|
17219
|
-
"description": "A placeholder for what's loading, sized by its container or by `width`\nand `height` in CSS. The shapes below extend it, and components with a\n`loading` attribute draw their own from them, so a spinner is only needed\nfor an action in progress. It's hidden from assistive technology, so mark\nthe region it sits in `aria-busy`.",
|
|
17354
|
+
"description": "A placeholder for what's loading, sized by its container or by `width`\nand `height` in CSS. The shapes below extend it, and components with a\n`loading` attribute draw their own from them, so a spinner is only needed\nfor an action in progress. It stays invisible for `--gw-motion-delay`, then\nfades in and pulses, so a quick load never flashes it; it keeps its size\nthroughout, so nothing moves. It's hidden from assistive technology, so mark\nthe region it sits in `aria-busy`.",
|
|
17220
17355
|
"name": "GwSkeleton",
|
|
17221
17356
|
"cssProperties": [
|
|
17222
17357
|
{
|
|
@@ -17226,6 +17361,10 @@
|
|
|
17226
17361
|
{
|
|
17227
17362
|
"description": "The corners. Defaults to the control radius.",
|
|
17228
17363
|
"name": "--gw-skeleton-radius"
|
|
17364
|
+
},
|
|
17365
|
+
{
|
|
17366
|
+
"description": "How long it waits before fading in. Defaults to `500ms`; set `0ms` to show it at once.",
|
|
17367
|
+
"name": "--gw-motion-delay"
|
|
17229
17368
|
}
|
|
17230
17369
|
],
|
|
17231
17370
|
"members": [
|
|
@@ -17236,7 +17375,7 @@
|
|
|
17236
17375
|
"text": "readonly CSSStyleSheet[]"
|
|
17237
17376
|
},
|
|
17238
17377
|
"static": true,
|
|
17239
|
-
"default": "[ base, css` :host { display: block; min-height: 1rem; background: var( --gw-skeleton-color, var(--gw-color-fill-neutral-resting) ); border-radius: var(--gw-skeleton-radius, var(--gw-radius-control)); animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 50% { opacity: 0.45; } } @media (forced-colors: active) { :host { background: GrayText; } } `, ]",
|
|
17378
|
+
"default": "[ base, css` :host { display: block; min-height: 1rem; background: var( --gw-skeleton-color, var(--gw-color-fill-neutral-resting) ); border-radius: var(--gw-skeleton-radius, var(--gw-radius-control)); /* Waits, fades in, then pulses, so a quick load never flashes it */ animation: gw-appear var(--gw-motion-slow, 350ms) cubic-bezier(0.4, 0, 0.2, 1) var(--gw-motion-delay, 500ms) backwards, pulse 2s ease-in-out calc(var(--gw-motion-delay, 500ms) + var(--gw-motion-slow, 350ms)) infinite; } @keyframes pulse { 50% { opacity: 0.45; } } @media (forced-colors: active) { :host { background: GrayText; } } `, ]",
|
|
17240
17379
|
"inheritedFrom": {
|
|
17241
17380
|
"name": "GwElement",
|
|
17242
17381
|
"module": "src/element.ts"
|
|
@@ -17599,6 +17738,14 @@
|
|
|
17599
17738
|
"name": "GwSkeleton",
|
|
17600
17739
|
"module": "src/gw-skeleton.ts"
|
|
17601
17740
|
}
|
|
17741
|
+
},
|
|
17742
|
+
{
|
|
17743
|
+
"description": "How long it waits before fading in. Defaults to `500ms`; set `0ms` to show it at once.",
|
|
17744
|
+
"name": "--gw-motion-delay",
|
|
17745
|
+
"inheritedFrom": {
|
|
17746
|
+
"name": "GwSkeleton",
|
|
17747
|
+
"module": "src/gw-skeleton.ts"
|
|
17748
|
+
}
|
|
17602
17749
|
}
|
|
17603
17750
|
],
|
|
17604
17751
|
"members": [
|
|
@@ -18346,6 +18493,14 @@
|
|
|
18346
18493
|
"name": "GwSkeleton",
|
|
18347
18494
|
"module": "src/gw-skeleton.ts"
|
|
18348
18495
|
}
|
|
18496
|
+
},
|
|
18497
|
+
{
|
|
18498
|
+
"description": "How long it waits before fading in. Defaults to `500ms`; set `0ms` to show it at once.",
|
|
18499
|
+
"name": "--gw-motion-delay",
|
|
18500
|
+
"inheritedFrom": {
|
|
18501
|
+
"name": "GwSkeleton",
|
|
18502
|
+
"module": "src/gw-skeleton.ts"
|
|
18503
|
+
}
|
|
18349
18504
|
}
|
|
18350
18505
|
],
|
|
18351
18506
|
"members": [
|
|
@@ -18737,12 +18892,16 @@
|
|
|
18737
18892
|
"declarations": [
|
|
18738
18893
|
{
|
|
18739
18894
|
"kind": "class",
|
|
18740
|
-
"description": "A spinning ring while something loads, announced by its `label`.",
|
|
18895
|
+
"description": "A spinning ring while something loads, announced by its `label`. It keeps\nits space but stays invisible for `--gw-motion-delay`, then fades in, so a\nquick wait never flashes it.",
|
|
18741
18896
|
"name": "GwSpinner",
|
|
18742
18897
|
"cssProperties": [
|
|
18743
18898
|
{
|
|
18744
18899
|
"description": "The ring's size. Defaults to `1.25rem`.",
|
|
18745
18900
|
"name": "--gw-spinner-size"
|
|
18901
|
+
},
|
|
18902
|
+
{
|
|
18903
|
+
"description": "How long it waits before fading in. Defaults to `500ms`.",
|
|
18904
|
+
"name": "--gw-motion-delay"
|
|
18746
18905
|
}
|
|
18747
18906
|
],
|
|
18748
18907
|
"cssParts": [
|
|
@@ -18772,7 +18931,7 @@
|
|
|
18772
18931
|
"text": "readonly CSSStyleSheet[]"
|
|
18773
18932
|
},
|
|
18774
18933
|
"static": true,
|
|
18775
|
-
"default": "[ base, css` :host { display: inline-flex; width: var(--gw-spinner-size, 1.25rem); height: var(--gw-spinner-size, 1.25rem); color: var(--gw-color-accent); } .ring { width: 100%; height: 100%; border: 2px solid var(--gw-color-border-quiet); border-top-color: currentColor; border-radius: 50%; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } `, ]",
|
|
18934
|
+
"default": "[ base, css` :host { display: inline-flex; width: var(--gw-spinner-size, 1.25rem); height: var(--gw-spinner-size, 1.25rem); color: var(--gw-color-accent); animation: gw-appear var(--gw-motion-slow, 350ms) cubic-bezier(0.4, 0, 0.2, 1) var(--gw-motion-delay, 500ms) backwards; } .ring { width: 100%; height: 100%; border: 2px solid var(--gw-color-border-quiet); border-top-color: currentColor; border-radius: 50%; animation: spin 0.9s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } `, ]",
|
|
18776
18935
|
"inheritedFrom": {
|
|
18777
18936
|
"name": "GwElement",
|
|
18778
18937
|
"module": "src/element.ts"
|
|
@@ -22959,7 +23118,7 @@
|
|
|
22959
23118
|
{
|
|
22960
23119
|
"kind": "variable",
|
|
22961
23120
|
"name": "base",
|
|
22962
|
-
"default": "css` :host { font-family: var(--gw-font-sans); color: var(--gw-color-text-primary); } *, *::before, *::after { box-sizing: border-box; } :host([hidden]), [hidden] { display: none !important; } :focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } .icon { flex: none; width: var(--gw-icon-size, 1.25rem); height: var(--gw-icon-size, 1.25rem); fill: currentColor; } :host([tone=\"accent\"]) { --gw-tone: var(--gw-color-accent); --gw-tone-fill: var(--gw-color-fill-accent-quiet); --gw-tone-border: var(--gw-color-border-accent); } :host([tone=\"info\"]) { --gw-tone: var(--gw-color-info); --gw-tone-fill: var(--gw-color-fill-info-quiet); --gw-tone-border: var(--gw-color-border-info); } :host([tone=\"success\"]) { --gw-tone: var(--gw-color-success); --gw-tone-fill: var(--gw-color-fill-success-quiet); --gw-tone-border: var(--gw-color-border-success); } :host([tone=\"warning\"]) { --gw-tone: var(--gw-color-warning); --gw-tone-fill: var(--gw-color-fill-warning-quiet); --gw-tone-border: var(--gw-color-border-warning); } :host([tone=\"danger\"]) { --gw-tone: var(--gw-color-danger); --gw-tone-fill: var(--gw-color-fill-danger-quiet); --gw-tone-border: var(--gw-color-border-danger); } :host([tone=\"important\"]) { --gw-tone: var(--gw-color-important); --gw-tone-fill: var(--gw-color-fill-important-quiet); --gw-tone-border: var(--gw-color-border-important); } /* :host too, since * in a shadow root doesn't match it, and the skeletons pulse on their host */ @media (prefers-reduced-motion: reduce) { :host, *, *::before, *::after { animation-duration: 0s !important; transition-duration: 0s !important; } } `",
|
|
23121
|
+
"default": "css` :host { font-family: var(--gw-font-sans); color: var(--gw-color-text-primary); } *, *::before, *::after { box-sizing: border-box; } :host([hidden]), [hidden] { display: none !important; } :focus-visible { outline: var(--gw-focus-width) solid var(--gw-color-focus); outline-offset: var(--gw-focus-offset); } .icon { flex: none; width: var(--gw-icon-size, 1.25rem); height: var(--gw-icon-size, 1.25rem); fill: currentColor; } :host([tone=\"accent\"]) { --gw-tone: var(--gw-color-accent); --gw-tone-fill: var(--gw-color-fill-accent-quiet); --gw-tone-border: var(--gw-color-border-accent); } :host([tone=\"info\"]) { --gw-tone: var(--gw-color-info); --gw-tone-fill: var(--gw-color-fill-info-quiet); --gw-tone-border: var(--gw-color-border-info); } :host([tone=\"success\"]) { --gw-tone: var(--gw-color-success); --gw-tone-fill: var(--gw-color-fill-success-quiet); --gw-tone-border: var(--gw-color-border-success); } :host([tone=\"warning\"]) { --gw-tone: var(--gw-color-warning); --gw-tone-fill: var(--gw-color-fill-warning-quiet); --gw-tone-border: var(--gw-color-border-warning); } :host([tone=\"danger\"]) { --gw-tone: var(--gw-color-danger); --gw-tone-fill: var(--gw-color-fill-danger-quiet); --gw-tone-border: var(--gw-color-border-danger); } :host([tone=\"important\"]) { --gw-tone: var(--gw-color-important); --gw-tone-fill: var(--gw-color-fill-important-quiet); --gw-tone-border: var(--gw-color-border-important); } /* A loading placeholder's entrance: hidden for --gw-motion-delay, so a quick load never flashes one, then a fade. Use it with appear below. */ @keyframes gw-appear { from { opacity: 0; } } /* :host too, since * in a shadow root doesn't match it, and the skeletons pulse on their host */ @media (prefers-reduced-motion: reduce) { :host, *, *::before, *::after { animation-duration: 0s !important; transition-duration: 0s !important; } } `",
|
|
22963
23122
|
"description": "What every component shares: box sizing, the focus ring, icons and the\ntones that set `--gw-tone` from a `tone` attribute."
|
|
22964
23123
|
},
|
|
22965
23124
|
{
|
package/package.json
CHANGED
package/src/gw-button.ts
CHANGED
|
@@ -295,14 +295,18 @@ export class GwButton extends GwElement {
|
|
|
295
295
|
cursor: progress;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/* The icon stays for --gw-motion-delay, so a quick action never
|
|
299
|
+
flashes the ring, then they crossfade; ending is immediate */
|
|
298
300
|
:host([loading]) .lead > .ring,
|
|
299
301
|
:host([loading]) .button:not(.has-icon) > .ring.over {
|
|
300
302
|
opacity: 1;
|
|
303
|
+
transition-delay: var(--gw-motion-delay, 500ms);
|
|
301
304
|
}
|
|
302
305
|
|
|
303
306
|
:host([loading]) .lead > :not(.ring),
|
|
304
307
|
:host([loading]) .button:not(.has-icon) > .text {
|
|
305
308
|
opacity: 0;
|
|
309
|
+
transition-delay: var(--gw-motion-delay, 500ms);
|
|
306
310
|
}
|
|
307
311
|
|
|
308
312
|
.button:not(.has-icon) > .lead {
|
package/src/gw-card.ts
CHANGED
|
@@ -25,6 +25,8 @@ import { base } from "./styles.js";
|
|
|
25
25
|
* @summary A surface for one group of content, or a link to more.
|
|
26
26
|
*
|
|
27
27
|
* @attr {string} heading - The card's heading.
|
|
28
|
+
* @attr {string} description - A line under the heading.
|
|
29
|
+
* @attr {number} level - The heading's level for assistive tech, 2 by default. Use 3 for a card under a section heading.
|
|
28
30
|
* @attr {string} href - Makes the whole card a link to this address.
|
|
29
31
|
* @attr {boolean} flush - Drops the padding, for content that runs to the edges.
|
|
30
32
|
* @attr {boolean} loading - Shows skeleton lines for the content and any unknown heading.
|
|
@@ -33,15 +35,20 @@ import { base } from "./styles.js";
|
|
|
33
35
|
* @slot - The content.
|
|
34
36
|
* @slot heading - The heading, in place of `heading`. An `<a>` here makes the whole card follow it.
|
|
35
37
|
* @slot actions - Buttons beside the heading.
|
|
38
|
+
* @slot footer - Content under a divider at the bottom, such as a control for the whole card.
|
|
36
39
|
*
|
|
37
40
|
* @csspart card - The card's surface.
|
|
38
41
|
* @csspart header - The row with the heading and actions.
|
|
39
42
|
* @csspart heading - The heading.
|
|
43
|
+
* @csspart description - The line under the heading.
|
|
40
44
|
* @csspart body - The content.
|
|
45
|
+
* @csspart footer - The area under the divider.
|
|
41
46
|
*/
|
|
42
47
|
export class GwCard extends GwElement {
|
|
43
48
|
static override properties = {
|
|
44
49
|
heading: {},
|
|
50
|
+
description: {},
|
|
51
|
+
level: { type: "number" },
|
|
45
52
|
href: {},
|
|
46
53
|
flush: { type: "boolean", reflect: true },
|
|
47
54
|
loading: { type: "boolean", reflect: true },
|
|
@@ -110,14 +117,41 @@ export class GwCard extends GwElement {
|
|
|
110
117
|
align-items: center;
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
.titles {
|
|
121
|
+
display: flex;
|
|
114
122
|
flex: 1;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
gap: var(--gw-space-0-5);
|
|
125
|
+
min-width: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.title {
|
|
115
129
|
margin: 0;
|
|
116
130
|
font-size: var(--gw-font-size-m);
|
|
117
131
|
font-weight: var(--gw-font-weight-strong);
|
|
118
132
|
color: var(--gw-color-text-strong);
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
.description {
|
|
136
|
+
font-size: var(--gw-font-size-xs);
|
|
137
|
+
color: var(--gw-color-text-secondary);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* The divider runs to the card's edges */
|
|
141
|
+
footer:not([hidden]) {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
gap: inherit;
|
|
145
|
+
margin-inline: calc(-1 * var(--gw-space-4));
|
|
146
|
+
padding: var(--gw-space-3) var(--gw-space-4) 0;
|
|
147
|
+
border-top: var(--gw-border-width) solid var(--gw-color-border-normal);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
:host([flush]) footer:not([hidden]) {
|
|
151
|
+
margin-inline: 0;
|
|
152
|
+
padding-bottom: var(--gw-space-3);
|
|
153
|
+
}
|
|
154
|
+
|
|
121
155
|
.body:not([hidden]) {
|
|
122
156
|
display: flex;
|
|
123
157
|
flex-direction: column;
|
|
@@ -127,6 +161,9 @@ export class GwCard extends GwElement {
|
|
|
127
161
|
];
|
|
128
162
|
|
|
129
163
|
declare heading: string;
|
|
164
|
+
declare description: string;
|
|
165
|
+
/** The heading's level for assistive tech. */
|
|
166
|
+
declare level: number;
|
|
130
167
|
declare href: string;
|
|
131
168
|
declare flush: boolean;
|
|
132
169
|
declare loading: boolean;
|
|
@@ -139,19 +176,34 @@ export class GwCard extends GwElement {
|
|
|
139
176
|
|
|
140
177
|
readonly #headingSlot = h("slot", { name: "heading" });
|
|
141
178
|
|
|
179
|
+
readonly #heading = h(
|
|
180
|
+
"div",
|
|
181
|
+
{ class: "title", part: "heading", role: "heading", "aria-level": "2" },
|
|
182
|
+
this.#title,
|
|
183
|
+
this.#headingSlot,
|
|
184
|
+
this.#titleSkeleton,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
readonly #description = h("span", {
|
|
188
|
+
class: "description",
|
|
189
|
+
part: "description",
|
|
190
|
+
});
|
|
191
|
+
|
|
142
192
|
readonly #header: HTMLElement = h(
|
|
143
193
|
"header",
|
|
144
194
|
{ part: "header" },
|
|
145
|
-
h(
|
|
146
|
-
"h2",
|
|
147
|
-
{ part: "heading" },
|
|
148
|
-
this.#title,
|
|
149
|
-
this.#headingSlot,
|
|
150
|
-
this.#titleSkeleton,
|
|
151
|
-
),
|
|
195
|
+
h("div", { class: "titles" }, this.#heading, this.#description),
|
|
152
196
|
h("slot", { name: "actions" }),
|
|
153
197
|
);
|
|
154
198
|
|
|
199
|
+
readonly #footerSlot = h("slot", { name: "footer" });
|
|
200
|
+
|
|
201
|
+
readonly #footer: HTMLElement = h(
|
|
202
|
+
"footer",
|
|
203
|
+
{ part: "footer", hidden: "" },
|
|
204
|
+
this.#footerSlot,
|
|
205
|
+
);
|
|
206
|
+
|
|
155
207
|
readonly #skeleton = h("gw-skeleton-lines");
|
|
156
208
|
|
|
157
209
|
readonly #body: HTMLDivElement = h(
|
|
@@ -169,6 +221,8 @@ export class GwCard extends GwElement {
|
|
|
169
221
|
constructor() {
|
|
170
222
|
super();
|
|
171
223
|
this.heading = "";
|
|
224
|
+
this.description = "";
|
|
225
|
+
this.level = 2;
|
|
172
226
|
this.href = "";
|
|
173
227
|
this.flush = false;
|
|
174
228
|
this.loading = false;
|
|
@@ -215,6 +269,11 @@ export class GwCard extends GwElement {
|
|
|
215
269
|
this.#headingSlot.addEventListener("slotchange", () => {
|
|
216
270
|
this.requestUpdate("heading");
|
|
217
271
|
});
|
|
272
|
+
|
|
273
|
+
this.#footerSlot.addEventListener("slotchange", () => {
|
|
274
|
+
this.#footer.hidden =
|
|
275
|
+
this.#footerSlot.assignedNodes({ flatten: true }).length === 0;
|
|
276
|
+
});
|
|
218
277
|
}
|
|
219
278
|
|
|
220
279
|
/** The `<a>` in the heading slot, or holding it, when there is one. */
|
|
@@ -227,7 +286,7 @@ export class GwCard extends GwElement {
|
|
|
227
286
|
}
|
|
228
287
|
|
|
229
288
|
protected override create(root: ShadowRoot) {
|
|
230
|
-
this.#card.append(this.#header, this.#skeleton, this.#body);
|
|
289
|
+
this.#card.append(this.#header, this.#skeleton, this.#body, this.#footer);
|
|
231
290
|
root.append(this.#card);
|
|
232
291
|
}
|
|
233
292
|
|
|
@@ -260,9 +319,14 @@ export class GwCard extends GwElement {
|
|
|
260
319
|
|
|
261
320
|
this.#card.classList.toggle("linked", this.#link() !== null && !this.href);
|
|
262
321
|
this.#title.textContent = this.heading;
|
|
322
|
+
this.#heading.setAttribute("aria-level", String(this.level || 2));
|
|
323
|
+
this.#description.textContent = this.description;
|
|
324
|
+
this.#description.hidden = !this.description;
|
|
263
325
|
this.#titleSkeleton.hidden =
|
|
264
326
|
this.heading !== "" || slotted || !this.loading;
|
|
265
|
-
this.#header.hidden =
|
|
327
|
+
this.#header.hidden =
|
|
328
|
+
!this.heading && !this.description && !slotted && !this.loading;
|
|
329
|
+
this.#heading.hidden = !this.heading && !slotted && !this.loading;
|
|
266
330
|
this.#skeleton.lines = this.lines;
|
|
267
331
|
this.#skeleton.hidden = !this.loading;
|
|
268
332
|
this.#body.hidden = this.loading;
|
package/src/gw-menu-button.ts
CHANGED
|
@@ -139,19 +139,50 @@ export class GwMenuButton extends GwElement {
|
|
|
139
139
|
background: var(--gw-color-fill-neutral-hovered);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
/* While a chosen action runs, a ring takes the icon's place
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
/* While a chosen action runs, a ring takes the icon's place: they share
|
|
143
|
+
one cell and crossfade after --gw-motion-delay, so a quick action
|
|
144
|
+
never flashes it and nothing moves. Without an icon, the ring sits
|
|
145
|
+
over the hidden text. */
|
|
146
|
+
.lead {
|
|
147
|
+
display: grid;
|
|
145
148
|
flex: none;
|
|
149
|
+
place-items: center;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.trigger:not(.has-icon) > .lead {
|
|
153
|
+
display: contents;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.lead > * {
|
|
157
|
+
grid-area: 1 / 1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.lead > *,
|
|
161
|
+
.label {
|
|
162
|
+
transition: opacity var(--gw-motion-normal) cubic-bezier(0.4, 0, 0.2, 1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.ring {
|
|
146
166
|
width: calc(var(--gw-icon-size) * 0.8);
|
|
147
167
|
height: calc(var(--gw-icon-size) * 0.8);
|
|
148
168
|
border: calc(2px * var(--gw-scale)) solid
|
|
149
169
|
color-mix(in oklch, currentColor 25%, transparent);
|
|
150
170
|
border-top-color: currentColor;
|
|
151
171
|
border-radius: 50%;
|
|
172
|
+
opacity: 0;
|
|
152
173
|
animation: spin 0.9s linear infinite;
|
|
153
174
|
}
|
|
154
175
|
|
|
176
|
+
.trigger:not(.has-icon) .ring {
|
|
177
|
+
position: absolute;
|
|
178
|
+
inset: 0;
|
|
179
|
+
margin: auto;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.trigger {
|
|
183
|
+
position: relative;
|
|
184
|
+
}
|
|
185
|
+
|
|
155
186
|
@keyframes spin {
|
|
156
187
|
to {
|
|
157
188
|
transform: rotate(360deg);
|
|
@@ -163,11 +194,14 @@ export class GwMenuButton extends GwElement {
|
|
|
163
194
|
}
|
|
164
195
|
|
|
165
196
|
:host([loading]) .ring {
|
|
166
|
-
|
|
197
|
+
opacity: 1;
|
|
198
|
+
transition-delay: var(--gw-motion-delay, 500ms);
|
|
167
199
|
}
|
|
168
200
|
|
|
169
|
-
:host([loading]) .
|
|
170
|
-
|
|
201
|
+
:host([loading]) .lead > .icon,
|
|
202
|
+
:host([loading]) .trigger:not(.has-icon) > .label {
|
|
203
|
+
opacity: 0;
|
|
204
|
+
transition-delay: var(--gw-motion-delay, 500ms);
|
|
171
205
|
}
|
|
172
206
|
|
|
173
207
|
/* The other appearances draw the trigger as gw-button draws a neutral
|
|
@@ -689,8 +723,12 @@ export class GwMenuButton extends GwElement {
|
|
|
689
723
|
|
|
690
724
|
protected override create(root: ShadowRoot) {
|
|
691
725
|
this.#trigger.append(
|
|
692
|
-
|
|
693
|
-
|
|
726
|
+
h(
|
|
727
|
+
"span",
|
|
728
|
+
{ class: "lead" },
|
|
729
|
+
this.#triggerIcon,
|
|
730
|
+
h("span", { class: "ring", "aria-hidden": "true" }),
|
|
731
|
+
),
|
|
694
732
|
this.#triggerText,
|
|
695
733
|
h("span", { class: "chevron", "aria-hidden": "true" }),
|
|
696
734
|
);
|
package/src/gw-skeleton.ts
CHANGED
|
@@ -12,7 +12,9 @@ import { base } from "./styles.js";
|
|
|
12
12
|
* A placeholder for what's loading, sized by its container or by `width`
|
|
13
13
|
* and `height` in CSS. The shapes below extend it, and components with a
|
|
14
14
|
* `loading` attribute draw their own from them, so a spinner is only needed
|
|
15
|
-
* for an action in progress. It
|
|
15
|
+
* for an action in progress. It stays invisible for `--gw-motion-delay`, then
|
|
16
|
+
* fades in and pulses, so a quick load never flashes it; it keeps its size
|
|
17
|
+
* throughout, so nothing moves. It's hidden from assistive technology, so mark
|
|
16
18
|
* the region it sits in `aria-busy`.
|
|
17
19
|
*
|
|
18
20
|
* @element gw-skeleton
|
|
@@ -20,6 +22,7 @@ import { base } from "./styles.js";
|
|
|
20
22
|
*
|
|
21
23
|
* @cssprop --gw-skeleton-color - The fill. Defaults to the neutral fill.
|
|
22
24
|
* @cssprop --gw-skeleton-radius - The corners. Defaults to the control radius.
|
|
25
|
+
* @cssprop --gw-motion-delay - How long it waits before fading in. Defaults to `500ms`; set `0ms` to show it at once.
|
|
23
26
|
*/
|
|
24
27
|
export class GwSkeleton extends GwElement {
|
|
25
28
|
static override styles: readonly CSSStyleSheet[] = [
|
|
@@ -33,7 +36,13 @@ export class GwSkeleton extends GwElement {
|
|
|
33
36
|
var(--gw-color-fill-neutral-resting)
|
|
34
37
|
);
|
|
35
38
|
border-radius: var(--gw-skeleton-radius, var(--gw-radius-control));
|
|
36
|
-
|
|
39
|
+
/* Waits, fades in, then pulses, so a quick load never flashes it */
|
|
40
|
+
animation:
|
|
41
|
+
gw-appear var(--gw-motion-slow, 350ms) cubic-bezier(0.4, 0, 0.2, 1)
|
|
42
|
+
var(--gw-motion-delay, 500ms) backwards,
|
|
43
|
+
pulse 2s ease-in-out
|
|
44
|
+
calc(var(--gw-motion-delay, 500ms) + var(--gw-motion-slow, 350ms))
|
|
45
|
+
infinite;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
@keyframes pulse {
|
package/src/gw-spinner.ts
CHANGED
|
@@ -8,7 +8,9 @@ import { GwElement, css, h } from "./element.js";
|
|
|
8
8
|
import { base } from "./styles.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A spinning ring while something loads, announced by its `label`.
|
|
11
|
+
* A spinning ring while something loads, announced by its `label`. It keeps
|
|
12
|
+
* its space but stays invisible for `--gw-motion-delay`, then fades in, so a
|
|
13
|
+
* quick wait never flashes it.
|
|
12
14
|
*
|
|
13
15
|
* @element gw-spinner
|
|
14
16
|
* @summary A spinning ring for a wait a skeleton can't stand in for.
|
|
@@ -18,6 +20,7 @@ import { base } from "./styles.js";
|
|
|
18
20
|
* @csspart ring - The spinning ring.
|
|
19
21
|
*
|
|
20
22
|
* @cssprop --gw-spinner-size - The ring's size. Defaults to `1.25rem`.
|
|
23
|
+
* @cssprop --gw-motion-delay - How long it waits before fading in. Defaults to `500ms`.
|
|
21
24
|
*/
|
|
22
25
|
export class GwSpinner extends GwElement {
|
|
23
26
|
static override properties = {
|
|
@@ -32,6 +35,8 @@ export class GwSpinner extends GwElement {
|
|
|
32
35
|
width: var(--gw-spinner-size, 1.25rem);
|
|
33
36
|
height: var(--gw-spinner-size, 1.25rem);
|
|
34
37
|
color: var(--gw-color-accent);
|
|
38
|
+
animation: gw-appear var(--gw-motion-slow, 350ms)
|
|
39
|
+
cubic-bezier(0.4, 0, 0.2, 1) var(--gw-motion-delay, 500ms) backwards;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
.ring {
|
package/src/styles.ts
CHANGED
|
@@ -101,6 +101,14 @@ export const base = css`
|
|
|
101
101
|
--gw-tone-border: var(--gw-color-border-important);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/* A loading placeholder's entrance: hidden for --gw-motion-delay, so a
|
|
105
|
+
quick load never flashes one, then a fade. Use it with appear below. */
|
|
106
|
+
@keyframes gw-appear {
|
|
107
|
+
from {
|
|
108
|
+
opacity: 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
/* :host too, since * in a shadow root doesn't match it, and the skeletons
|
|
105
113
|
pulse on their host */
|
|
106
114
|
@media (prefers-reduced-motion: reduce) {
|