srcdev-nuxt-components 9.1.55 → 9.1.57

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 (104) hide show
  1. package/.claude/settings.json +30 -2
  2. package/.claude/skills/components/alert-content-inner.md +60 -0
  3. package/.claude/skills/components/display-dialog.md +80 -0
  4. package/.claude/skills/components/display-prompt.md +163 -0
  5. package/.claude/skills/components/display-toast.md +235 -0
  6. package/.claude/skills/index.md +7 -3
  7. package/.claude/skills/theming-colour-ramps.md +328 -0
  8. package/.claude/skills/theming-override-default.md +115 -186
  9. package/.claude/skills/theming-partial-override.md +88 -75
  10. package/README.md +166 -0
  11. package/app/app.config.ts +44 -0
  12. package/app/assets/styles/setup/01.config/_head.css +3 -3
  13. package/app/assets/styles/setup/02.colours/_amber.css +12 -10
  14. package/app/assets/styles/setup/02.colours/_blue.css +10 -9
  15. package/app/assets/styles/setup/02.colours/_green.css +12 -11
  16. package/app/assets/styles/setup/02.colours/_orange.css +12 -10
  17. package/app/assets/styles/setup/02.colours/_red.css +10 -9
  18. package/app/assets/styles/setup/02.colours/_slate.css +12 -11
  19. package/app/assets/styles/setup/02.colours/_sunset.css +12 -10
  20. package/app/assets/styles/setup/02.colours/_theme-params.css +18 -0
  21. package/app/assets/styles/setup/02.colours/index.css +1 -0
  22. package/app/assets/styles/setup/03.theming/_default.css +87 -0
  23. package/app/assets/styles/setup/03.theming/_error.css +14 -0
  24. package/app/assets/styles/setup/03.theming/_success.css +4 -0
  25. package/app/assets/styles/setup/03.theming/_theme-slots.css +16 -0
  26. package/app/assets/styles/setup/03.theming/_warning.css +13 -0
  27. package/app/assets/styles/setup/03.theming/index.css +6 -4
  28. package/app/assets/styles/setup/03.theming/theme-ramp.css +19 -0
  29. package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +0 -7
  30. package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
  31. package/app/assets/styles/setup/index.css +1 -1
  32. package/app/components/01.atoms/display-dialog/DisplayDialog.vue +74 -24
  33. package/app/components/01.atoms/display-dialog/tests/DisplayDialog.spec.ts +148 -14
  34. package/app/components/01.atoms/display-dialog/tests/__snapshots__/DisplayDialog.spec.ts.snap +3 -3
  35. package/app/components/01.atoms/prompt/DisplayPrompt.vue +113 -0
  36. package/app/components/01.atoms/prompt/stories/DisplayPrompt.stories.ts +112 -0
  37. package/app/components/01.atoms/prompt/tests/DisplayPrompt.spec.ts +205 -0
  38. package/app/components/01.atoms/toast/DisplayToast.vue +327 -0
  39. package/app/components/01.atoms/toast/DisplayToastProvider.vue +362 -0
  40. package/app/components/01.atoms/toast/stories/DisplayToast.stories.ts +195 -0
  41. package/app/components/01.atoms/toast/stories/DisplayToastProvider.stories.ts +142 -0
  42. package/app/components/01.atoms/toast/tests/DisplayToast.spec.ts +266 -0
  43. package/app/components/01.atoms/toast/tests/DisplayToastProvider.spec.ts +256 -0
  44. package/app/components/02.molecules/action-menu/ActionMenu.vue +8 -1
  45. package/app/components/02.molecules/action-menu/ActionMenuItemCore.vue +11 -3
  46. package/app/components/02.molecules/alert-content/AlertContent.vue +67 -0
  47. package/app/components/02.molecules/alert-content/AlertContentInner.vue +153 -0
  48. package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +131 -0
  49. package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +135 -0
  50. package/app/components/02.molecules/alert-content/tests/AlertContentInner.spec.ts +135 -0
  51. package/app/components/02.molecules/alert-masked-content/AlertMaskedContent.vue +78 -0
  52. package/app/components/02.molecules/alert-masked-content/stories/AlertMaskedContent.stories.ts +132 -0
  53. package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +149 -0
  54. package/app/components/05.forms/input-button/InputButtonCore.vue +35 -39
  55. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +8 -8
  56. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioCore.vue +5 -5
  57. package/app/components/05.forms/input-copy/InputCopyCore.vue +7 -7
  58. package/app/components/05.forms/input-number/InputNumberCore.vue +5 -5
  59. package/app/components/05.forms/input-select/InputSelectCore.vue +6 -6
  60. package/app/components/05.forms/input-text/InputTextCore.vue +6 -6
  61. package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +2 -2
  62. package/app/components/05.forms/input-textarea/InputTextareaCore.vue +5 -5
  63. package/app/components/05.forms/pending-effect/PendingEffect.vue +60 -60
  64. package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +10 -10
  65. package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +4 -4
  66. package/app/components/display-theme-switch/DisplayThemeSwitch.vue +4 -4
  67. package/app/components/display-tooltip/DisplayTooltip.vue +4 -4
  68. package/app/composables/useBodyLock.ts +21 -0
  69. package/app/composables/useDialogControls.ts +11 -11
  70. package/app/composables/useToastQueue.ts +39 -0
  71. package/app/layouts/default.vue +1 -1
  72. package/app/pages/ui/display-dialog.vue +275 -127
  73. package/app/pages/ui/display-prompt.vue +255 -52
  74. package/app/pages/ui/display-toast.vue +278 -299
  75. package/app/pages/ui/mask-element.vue +25 -3
  76. package/app/pages/ui/settings.vue +9 -35
  77. package/app/types/app-config.d.ts +41 -0
  78. package/app/types/components/display-prompt.d.ts +11 -0
  79. package/app/types/components/display-toast.d.ts +11 -9
  80. package/app/types/components/index.ts +2 -0
  81. package/app/types/components/semantic-theme.d.ts +1 -0
  82. package/nuxt.config.ts +16 -0
  83. package/package.json +8 -4
  84. package/ramps.config.mjs +28 -0
  85. package/scripts/check-ramps.mjs +50 -0
  86. package/scripts/generate-consumer-ramps.mjs +100 -0
  87. package/scripts/generate-ramps.mjs +92 -0
  88. package/app/assets/styles/setup/03.theming/default/_dark.css +0 -156
  89. package/app/assets/styles/setup/03.theming/default/_light.css +0 -166
  90. package/app/assets/styles/setup/03.theming/default/index.css +0 -2
  91. package/app/assets/styles/setup/03.theming/error/_dark.css +0 -56
  92. package/app/assets/styles/setup/03.theming/error/_light.css +0 -74
  93. package/app/assets/styles/setup/03.theming/error/index.css +0 -2
  94. package/app/assets/styles/setup/03.theming/success/_dark.css +0 -50
  95. package/app/assets/styles/setup/03.theming/success/_light.css +0 -65
  96. package/app/assets/styles/setup/03.theming/success/index.css +0 -2
  97. package/app/assets/styles/setup/03.theming/warning/_dark.css +0 -50
  98. package/app/assets/styles/setup/03.theming/warning/_light.css +0 -64
  99. package/app/assets/styles/setup/03.theming/warning/index.css +0 -2
  100. package/app/components/display-prompt/DisplayPromptCore.vue +0 -228
  101. package/app/components/display-prompt/variants/DisplayPromptError.vue +0 -53
  102. package/app/components/display-toast/DisplayToast.vue +0 -447
  103. package/app/components/display-toast/molecules/DefaultToastContent.vue +0 -163
  104. package/app/components/display-toast/stories/DisplayToast.stories.ts +0 -380
@@ -1,228 +0,0 @@
1
- <template>
2
- <div
3
- ref="promptElementRef"
4
- class="display-prompt-core"
5
- :class="[{ closed: !compopnentOpen }]"
6
- :data-test-id="`display-prompt-core-${theme}`"
7
- tabindex="0"
8
- >
9
- <div class="display-prompt-wrapper" :data-theme="theme" :class="[elementClasses]" data-test-id="display-prompt">
10
- <div class="display-prompt-inner">
11
- <div class="display-prompt-icon" data-test-id="prompt-icon" aria-hidden="true">
12
- <slot name="customDecoratorIcon">
13
- <Icon :name="displayPromptIcons[theme] ?? 'akar-icons:circle-alert'" class="icon" :color="iconColor" />
14
- </slot>
15
- </div>
16
- <div class="display-prompt-content" :aria-live="useAutoFocus ? 'polite' : undefined">
17
- <p class="title" data-test-id="display-prompt-title">
18
- <slot name="title"></slot>
19
- </p>
20
- <p v-if="slots.content" class="text" data-test-id="display-prompt-content">
21
- <slot name="content"></slot>
22
- </p>
23
- </div>
24
- <button
25
- v-if="dismissible"
26
- data-test-id="display-prompt-action"
27
- class="display-prompt-action"
28
- @click.prevent="updateComponentState()"
29
- >
30
- <slot name="customCloseIcon">
31
- <Icon name="bitcoin-icons:cross-filled" class="icon" />
32
- </slot>
33
- <span class="sr-only">
34
- <slot name="customTitle">Close this prompt</slot>
35
- </span>
36
- </button>
37
- </div>
38
- </div>
39
- </div>
40
- </template>
41
-
42
- <script setup lang="ts">
43
- const props = defineProps({
44
- dismissible: {
45
- type: Boolean,
46
- default: false,
47
- },
48
- theme: {
49
- type: String,
50
- default: "error",
51
- },
52
- styleClassPassthrough: {
53
- type: [String, Array] as PropType<string | string[]>,
54
- default: () => [],
55
- },
56
- iconColor: {
57
- type: String as PropType<string>,
58
- default: "dark-grey",
59
- },
60
- displayPromptIcons: {
61
- type: Object as PropType<Record<string, string>>,
62
- default: () => ({
63
- error: "akar-icons:circle-alert",
64
- info: "akar-icons:info",
65
- success: "akar-icons:check",
66
- warning: "akar-icons:circle-alert",
67
- secondary: "akar-icons:info",
68
- }),
69
- },
70
- useAutoFocus: {
71
- type: Boolean,
72
- default: false,
73
- },
74
- });
75
-
76
- const slots = useSlots();
77
- const promptElementRef = useTemplateRef<HTMLElement>("promptElementRef");
78
- const parentComponentState = defineModel<boolean>({ default: false });
79
- const compopnentOpen = ref(true);
80
- const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
81
-
82
- const updateComponentState = () => {
83
- if (parentComponentState.value) {
84
- parentComponentState.value = false;
85
- return;
86
- }
87
-
88
- compopnentOpen.value = false;
89
- };
90
-
91
- onMounted(async () => {
92
- if (props.useAutoFocus && promptElementRef.value) {
93
- promptElementRef.value.focus();
94
- }
95
- });
96
- </script>
97
-
98
- <style lang="css">
99
- @layer components {
100
- .display-prompt-core {
101
- display: grid;
102
- grid-template-rows: 1fr;
103
- opacity: 1;
104
- transition: all 200ms ease-in-out;
105
-
106
- &.closed {
107
- grid-template-rows: 0fr;
108
- opacity: 0;
109
- pointer-events: none;
110
- }
111
-
112
- .display-prompt-wrapper {
113
- background-color: var(--colour-theme-8);
114
- border: 0px solid transparent;
115
- border-radius: 4px;
116
- border-start-start-radius: 8px;
117
- border-end-start-radius: 8px;
118
- padding-inline-start: 8px;
119
-
120
- overflow: hidden;
121
-
122
- &:not(.dark) {
123
- &.outlined {
124
- border: 1px solid var(--colour-theme-8);
125
- }
126
- }
127
-
128
- &.dark {
129
- border-width: 0;
130
-
131
- .display-prompt-inner {
132
- background-color: var(--slate-01);
133
- }
134
-
135
- &.outlined {
136
- border: 1px solid var(--colour-theme-8);
137
- }
138
- }
139
-
140
- .display-prompt-inner {
141
- align-items: center;
142
- display: flex;
143
- gap: 12px;
144
- justify-content: space-between;
145
- padding-block: 1rem;
146
- padding-inline: 1.5rem;
147
-
148
- border-start-start-radius: 8px;
149
- border-end-start-radius: 8px;
150
- background-color: var(--colour-theme-10);
151
-
152
- .display-prompt-icon {
153
- display: inline-flex;
154
- .icon {
155
- color: var(--colour-theme-8);
156
- display: inline-block;
157
- font-size: 3rem;
158
- font-style: normal;
159
- font-weight: normal;
160
- overflow: hidden;
161
- }
162
- }
163
-
164
- .display-prompt-content {
165
- display: block flex;
166
- flex-direction: column;
167
- flex-grow: 1;
168
- gap: 1rem;
169
- margin: 0;
170
- padding: 0.2rem;
171
-
172
- .title {
173
- font-size: var(--step-5);
174
- font-weight: bold;
175
- line-height: 1.3;
176
- color: var(--colour-theme-8);
177
- margin: 0;
178
- padding: 0;
179
- }
180
-
181
- .text {
182
- font-size: var(--step-4);
183
- font-weight: normal;
184
- line-height: 1.3;
185
- color: var(--colour-theme-8);
186
- margin: 0;
187
- padding: 0;
188
- }
189
- }
190
- .display-prompt-action {
191
- background-color: transparent;
192
- display: block flex;
193
- align-items: center;
194
- justify-content: center;
195
- margin: 1rem;
196
- padding: 0.5rem;
197
- border: 0.1rem solid var(--colour-theme-8);
198
- border-radius: 50%;
199
- outline: 1px solid var(--colour-theme-3);
200
-
201
- transition:
202
- border 200ms ease-in-out,
203
- outline 200ms ease-in-out;
204
-
205
- &:hover {
206
- cursor: pointer;
207
- border: 0.1rem solid var(--colour-theme-12);
208
- outline: 2px solid var(--colour-theme-6);
209
- }
210
-
211
- &:focus-visible {
212
- box-shadow: var(--focus-box-shadow-colour-on);
213
- border: 0.1rem solid var(--colour-theme-12);
214
- outline: 2px solid var(--colour-theme-6);
215
- }
216
-
217
- .icon {
218
- color: var(--colour-theme-8);
219
- display: block;
220
- font-size: var(--step-5);
221
- padding: 1rem;
222
- }
223
- }
224
- }
225
- }
226
- }
227
- }
228
- </style>
@@ -1,53 +0,0 @@
1
- <template>
2
- <DisplayPromptCore variant="error" :dismissible icon-color="white" :style-class-passthrough>
3
- <template #icon>
4
- <Icon name="akar-icons:circle-alert" class="icon" />
5
- </template>
6
- <template #title>
7
- <slot name="title"></slot>
8
- </template>
9
- <template v-if="slots.content" #content>
10
- <slot name="content"></slot>
11
- </template>
12
- </DisplayPromptCore>
13
- </template>
14
-
15
- <script setup lang="ts">
16
- const props = defineProps({
17
- dismissible: {
18
- type: Boolean,
19
- default: false,
20
- },
21
- styleClassPassthrough: {
22
- type: [String, Array] as PropType<string | string[]>,
23
- default: () => [],
24
- },
25
- });
26
-
27
- const slots = useSlots();
28
- </script>
29
-
30
- <style lang="css">
31
- @layer components {
32
- .display-prompt.error {
33
- --bg-color: var(--red-02);
34
- --text-color: var(--slate-00);
35
- }
36
-
37
- .display-prompt {
38
- &.error {
39
- background-color: var(--bg-color);
40
- }
41
-
42
- &-icon {
43
- .icon {
44
- color: var(--text-color);
45
- }
46
- }
47
-
48
- &-content {
49
- color: var(--text-color);
50
- }
51
- }
52
- }
53
- </style>
@@ -1,447 +0,0 @@
1
- <template>
2
- <Teleport to="body">
3
- <div
4
- v-if="privateDisplayToast"
5
- ref="toastElementRef"
6
- class="display-toast"
7
- :class="[
8
- elementClasses,
9
- cssStateClass,
10
- positionClasses,
11
- {
12
- 'has-theme': !slots.default,
13
- },
14
- ]"
15
- :data-theme="theme"
16
- :role="toastRole"
17
- :aria-live="ariaLive"
18
- :tabindex="slots.default ? undefined : '0'"
19
- :aria-describedby="slots.default ? undefined : 'toast-message-' + toastId"
20
- @keydown.escape="setDismissToast"
21
- >
22
- <slot v-if="slots.default"></slot>
23
-
24
- <DefaultToastContent
25
- v-else
26
- :theme="theme"
27
- :custom-icon="customIcon"
28
- :toast-id="toastId"
29
- :toast-display-text="toastDisplayText"
30
- :toast-title="toastTitle"
31
- :toast-description="toastDescription"
32
- :auto-dismiss="autoDismiss"
33
- :set-dismiss-toast="setDismissToast"
34
- >
35
- <template #customToastIcon>
36
- <slot name="customToastIcon"></slot>
37
- </template>
38
- <template #title>
39
- <slot name="title"></slot>
40
- </template>
41
- <template #description>
42
- <slot name="description"></slot>
43
- </template>
44
- </DefaultToastContent>
45
- <div v-if="autoDismiss" class="display-toast-progress"></div>
46
- </div>
47
- </Teleport>
48
- </template>
49
-
50
- <script setup lang="ts">
51
- import type {
52
- DisplayToastConfig,
53
- DisplayToastTheme,
54
- DisplayToastPosition,
55
- DisplayToastAlignment,
56
- ToastSlots,
57
- } from "../../types/components";
58
-
59
- const props = defineProps({
60
- config: {
61
- type: Object as PropType<DisplayToastConfig>,
62
- default: () => ({
63
- appearance: {
64
- theme: "ghost" as DisplayToastTheme,
65
- position: "top" as DisplayToastPosition,
66
- alignment: "right" as DisplayToastAlignment,
67
- fullWidth: false,
68
- },
69
- behavior: {
70
- autoDismiss: true,
71
- duration: 5000,
72
- revealDuration: 550,
73
- },
74
- content: {
75
- text: "",
76
- customIcon: undefined,
77
- },
78
- }),
79
- },
80
- styleClassPassthrough: {
81
- type: [String, Array] as PropType<string | string[]>,
82
- default: () => [],
83
- },
84
- });
85
-
86
- const slots = defineSlots<ToastSlots>();
87
-
88
- const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
89
-
90
- // Computed properties for accessing config values with defaults
91
- const theme = computed(() => props.config?.appearance?.theme ?? "ghost");
92
- const position = computed(() => props.config?.appearance?.position ?? "top");
93
- const alignment = computed(() => props.config?.appearance?.alignment ?? "right");
94
- const fullWidth = computed(() => props.config?.appearance?.fullWidth ?? false);
95
- const autoDismiss = computed(() => props.config?.behavior?.autoDismiss ?? true);
96
- const duration = computed(() => props.config?.behavior?.duration ?? 5000);
97
- const revealDuration = computed(() => props.config?.behavior?.revealDuration ?? 550);
98
- const returnFocusTo = computed(() => props.config?.behavior?.returnFocusTo ?? null);
99
- const toastDisplayText = computed(() => props.config?.content?.text ?? "");
100
- const toastTitle = computed(() => props.config?.content?.title ?? "");
101
- const toastDescription = computed(() => props.config?.content?.description ?? "");
102
- const customIcon = computed(() => props.config?.content?.customIcon);
103
-
104
- // Computed classes for positioning
105
- const positionClasses = computed(() => {
106
- const classes = [];
107
- classes.push(position.value);
108
- if (fullWidth.value) {
109
- classes.push("full-width");
110
- } else {
111
- classes.push(alignment.value);
112
- }
113
- return classes;
114
- });
115
-
116
- /*
117
- * Accessibility setup
118
- */
119
- const toastId = useId();
120
- const toastElementRef = useTemplateRef<HTMLElement>("toastElementRef");
121
-
122
- // Determine appropriate ARIA attributes based on theme
123
- const toastRole = computed(() => {
124
- return ["error", "warning"].includes(theme.value) ? "alert" : "status";
125
- });
126
-
127
- const ariaLive = computed(() => {
128
- return ["error", "warning"].includes(theme.value) ? "assertive" : "polite";
129
- });
130
-
131
- /*
132
- * Setup component state
133
- */
134
- const externalTriggerModel = defineModel<boolean>({ default: false });
135
- const privateDisplayToast = ref(false);
136
- const transitionalState = ref(false);
137
- const cssStateClass = computed(() => {
138
- return transitionalState.value ? "show" : "hide";
139
- });
140
-
141
- /*
142
- * Computed properties for durations (in ms for CSS)
143
- */
144
- const revealDurationMs = computed(() => revealDuration.value + "ms");
145
- const displayDurationMs = computed(() => duration.value + "ms");
146
-
147
- /*
148
- * Lifecycle hooks
149
- */
150
- const setDismissToast = async () => {
151
- transitionalState.value = false;
152
- await useSleep(revealDuration.value);
153
-
154
- // Return focus to specified element if provided
155
- if (returnFocusTo.value) {
156
- // Handle both HTMLElement and ComponentPublicInstance
157
- let focusTarget: HTMLElement | null = null;
158
-
159
- if (returnFocusTo.value instanceof HTMLElement) {
160
- focusTarget = returnFocusTo.value;
161
- } else if (returnFocusTo.value && "$el" in returnFocusTo.value) {
162
- focusTarget = returnFocusTo.value.$el as HTMLElement;
163
- }
164
-
165
- if (focusTarget && typeof focusTarget.focus === "function") {
166
- focusTarget.focus();
167
- }
168
- }
169
-
170
- externalTriggerModel.value = false;
171
- privateDisplayToast.value = false;
172
- };
173
-
174
- watch(
175
- () => props.styleClassPassthrough,
176
- () => {
177
- resetElementClasses(props.styleClassPassthrough);
178
- }
179
- );
180
-
181
- watch(
182
- () => externalTriggerModel.value,
183
- async (newValue, previousValue) => {
184
- if (newValue) {
185
- privateDisplayToast.value = true;
186
- transitionalState.value = true;
187
-
188
- // Focus management for accessibility when not using custom slots
189
- if (!slots.default) {
190
- await nextTick();
191
- // Wait for animation to start before focusing
192
- setTimeout(() => {
193
- toastElementRef.value?.focus();
194
- }, 100);
195
- }
196
-
197
- if (autoDismiss.value) {
198
- await useSleep(duration.value);
199
- setDismissToast();
200
- }
201
- } else if (!newValue && previousValue) {
202
- // If external model is set to false, dismiss the toast
203
- setDismissToast();
204
- }
205
- }
206
- );
207
-
208
- onBeforeRouteLeave(() => {
209
- setDismissToast();
210
- });
211
- </script>
212
-
213
- <style lang="css">
214
- @layer components {
215
- @keyframes show {
216
- to {
217
- opacity: 1;
218
- transform: translateY(0);
219
- }
220
- }
221
-
222
- @keyframes hideTop {
223
- 0% {
224
- opacity: 1;
225
- transform: translateY(0);
226
- }
227
- 100% {
228
- opacity: 0;
229
- transform: translateY(-30px);
230
- }
231
- }
232
-
233
- @keyframes hideBottom {
234
- 0% {
235
- opacity: 1;
236
- transform: translateY(0);
237
- }
238
- 100% {
239
- opacity: 0;
240
- transform: translateY(30px);
241
- }
242
- }
243
-
244
- @keyframes progress {
245
- to {
246
- transform: scaleX(1);
247
- }
248
- }
249
-
250
- .display-toast {
251
- --_toast-gutter: 12px;
252
- @media (width >= 600px) {
253
- --_toast-gutter: 24px;
254
- }
255
-
256
- display: block;
257
- overflow: hidden;
258
- position: fixed;
259
- margin: 0;
260
- opacity: 0;
261
-
262
- z-index: 100;
263
-
264
- /* Focus styles for accessibility */
265
- &:focus {
266
- outline: 2px solid var(--colour-theme-3, #007acc);
267
- outline-offset: 2px;
268
- }
269
-
270
- &:focus:not(:focus-visible) {
271
- outline: none;
272
- }
273
-
274
- &.show {
275
- @supports (animation-timing-function: linear(0, 1)) {
276
- animation: show v-bind(revealDurationMs) var(--spring-easing) forwards;
277
- }
278
-
279
- @supports not (animation-timing-function: linear(0, 1)) {
280
- animation: show calc(v-bind(revealDurationMs) / 2) linear forwards;
281
- }
282
- }
283
-
284
- &.hide {
285
- @supports (animation-timing-function: linear(0, 1)) {
286
- animation: hideTop v-bind(revealDurationMs) var(--spring-easing) forwards;
287
- }
288
-
289
- @supports not (animation-timing-function: linear(0, 1)) {
290
- animation: hideTop calc(v-bind(revealDurationMs) / 2) linear forwards;
291
- }
292
-
293
- &.bottom {
294
- @supports (animation-timing-function: linear(0, 1)) {
295
- animation: hideBottom v-bind(revealDurationMs) var(--spring-easing) forwards;
296
- }
297
-
298
- @supports not (animation-timing-function: linear(0, 1)) {
299
- animation: hideBottom calc(v-bind(revealDurationMs) / 2) linear forwards;
300
- }
301
- }
302
- }
303
-
304
- /*
305
- * Default is centre for smaller screens
306
- */
307
- inset-inline: var(--_toast-gutter);
308
- margin-inline: auto;
309
-
310
- @media (width >= 600px) {
311
- &.left {
312
- inset-inline-start: var(--_toast-gutter);
313
- inset-inline-end: unset;
314
- }
315
-
316
- &.right {
317
- inset-inline-end: var(--_toast-gutter);
318
- inset-inline-start: unset;
319
- }
320
-
321
- &.center {
322
- &:not(.full-width) {
323
- inset-inline: 0;
324
- margin-inline: auto;
325
- width: max-content;
326
- }
327
- }
328
- }
329
-
330
- &.top {
331
- inset-block-start: var(--_toast-gutter);
332
- transform: translateY(-30px);
333
- }
334
- &.bottom {
335
- inset-block-end: var(--_toast-gutter);
336
- transform: translateY(30px);
337
- }
338
-
339
- /*
340
- * Styles for the display toast component if slot is empty
341
- */
342
- &.has-theme {
343
- padding-inline-start: 6px;
344
- background-color: var(--colour-theme-8);
345
-
346
- border: 0.1rem solid var(--colour-theme-8);
347
- border-start-start-radius: 8px;
348
- border-end-start-radius: 8px;
349
- border-start-end-radius: 4px;
350
- border-end-end-radius: 4px;
351
-
352
- overflow: hidden;
353
-
354
- /* .display-toast-inner {
355
- display: grid;
356
- grid-template-columns: auto 1fr auto;
357
- gap: 12px;
358
- align-items: center;
359
- background-color: var(--slate-10);
360
- border-start-start-radius: 8px;
361
- border-end-start-radius: 8px;
362
- padding: 12px 14px;
363
- overflow: hidden;
364
-
365
- .toast-icon {
366
- display: inline-flex;
367
- align-items: center;
368
- justify-content: center;
369
- margin-right: 12px;
370
-
371
- .icon {
372
- color: var(--colour-theme-0);
373
- display: inline-block;
374
- font-size: 2.5rem;
375
- font-style: normal;
376
- font-weight: normal;
377
- overflow: hidden;
378
- }
379
- }
380
-
381
- .toast-message {
382
- display: flex;
383
- align-items: center;
384
- font-size: var(--step-4);
385
- font-weight: normal;
386
- line-height: 1.3;
387
- color: var(--colour-theme-0);
388
- margin: 0;
389
- padding: 0;
390
- }
391
-
392
- .toast-action {
393
- display: flex;
394
- align-items: center;
395
- justify-content: center;
396
- margin-left: 12px;
397
-
398
- button {
399
- display: flex;
400
- align-items: center;
401
- justify-content: center;
402
- background: var(--colour-theme-10);
403
- border: 0.1rem solid var(--colour-theme-8);
404
- outline: 0.1rem solid transparent;
405
- border-radius: 50%;
406
- box-shadow: none;
407
- color: var(--colour-theme-0);
408
- cursor: pointer;
409
- font-size: var(--step-4);
410
- font-weight: bold;
411
- padding: 0.5rem;
412
- text-decoration: underline;
413
-
414
- transition: all 0.3s ease;
415
-
416
- .icon {
417
- font-size: 1.5rem;
418
- vertical-align: middle;
419
- }
420
-
421
- &:hover,
422
- &:focus-visible {
423
- box-shadow: none;
424
- background-color: var(--colour-theme-8);
425
- color: var(--colour-theme-0);
426
- outline: 0.1rem solid var(--colour-theme-3);
427
- outline-offset: 0.2rem;
428
- }
429
- }
430
- }
431
- } */
432
- }
433
-
434
- .display-toast-progress {
435
- position: absolute;
436
- inset-block-end: 4px;
437
- inset-inline: 15px 8px;
438
- height: 3px;
439
- transform: scaleX(0);
440
- transform-origin: right;
441
- background: linear-gradient(to right, var(--colour-theme-2), var(--colour-theme-8));
442
- border-radius: inherit;
443
- animation: progress v-bind(displayDurationMs) linear forwards;
444
- }
445
- }
446
- }
447
- </style>