@@ -82,18 +87,35 @@
flex-direction: row;
flex-wrap: wrap;
align-items: center;
- border-radius: var(--goa-border-radius-m);
- gap: var(--goa-space-m);
- padding: var(--goa-space-m) var(--goa-space-l);
- max-width: 640px;
- color: var(--goa-color-text-light);
- transition: transform 0.3s ease, opacity 0.3s ease;
+ border-radius: var(--goa-temporary-notification-borderRadius, var(--goa-border-radius-m));
+ gap: var(--goa-temporary-notification-row-gap, var(--goa-space-m)); /* 16px between content and action */
+ padding: var(--goa-temporary-notification-padding, var(--goa-space-m) var(--goa-space-l));
+ max-width: var(--goa-temporary-notification-max-width, 640px);
+ color: var(--goa-temporary-notification-color-text, var(--goa-color-text-light));
+ transition:
+ transform var(--goa-temporary-notification-transition-duration, 0.3s) ease,
+ opacity var(--goa-temporary-notification-transition-duration, 0.3s) ease;
overflow: hidden;
}
+ /* Add extra bottom padding when progress bar is present */
+ .snackbar.progress,
+ .snackbar.indeterminate {
+ padding: var(--goa-temporary-notification-padding-with-progress, var(--goa-space-m) var(--goa-space-l) 22px var(--goa-space-l));
+ }
+
+ /* Content wrapper keeps icon and message together as a single flex item */
+ .content {
+ display: flex;
+ align-items: flex-start; /* Icon aligns with first line of text */
+ gap: var(--goa-temporary-notification-column-gap, var(--goa-space-s));
+ flex: 1 1 auto;
+ min-width: 0; /* Allow content to shrink */
+ }
+
@media (--not-mobile) {
.snackbar {
- min-width: 360px;
+ min-width: var(--goa-temporary-notification-min-width-desktop, 360px);
}
}
@@ -107,8 +129,8 @@
.snackbar.basic,
.snackbar.indeterminate,
.snackbar.progress {
- border: 1px solid var(--goa-color-greyscale-700);
- background: var(--goa-color-greyscale-black);
+ border: var(--goa-temporary-notification-borderWidth, var(--goa-border-width-s)) solid var(--goa-temporary-notification-color-border, var(--goa-color-greyscale-700));
+ background: var(--goa-temporary-notification-color-bg-basic, var(--goa-color-greyscale-black));
}
.action {
@@ -123,7 +145,24 @@
bottom: 0;
left: 0;
width: 100%;
- height: 6px;
+ height: var(--goa-temporary-notification-progress-bar-height, 6px);
+ border-radius: 0 0 var(--goa-temporary-notification-progress-bar-borderRadius, 0) var(--goa-temporary-notification-progress-bar-borderRadius, 0);
+ }
+
+ /* Progress bar browser-specific styling */
+ progress::-webkit-progress-bar {
+ background-color: var(--goa-temporary-notification-progress-bar-color-bg, #adadad);
+ border-radius: 0 0 var(--goa-temporary-notification-progress-bar-borderRadius, 0) var(--goa-temporary-notification-progress-bar-borderRadius, 0);
+ }
+
+ progress::-webkit-progress-value {
+ background-color: var(--goa-temporary-notification-progress-bar-color-fill, white);
+ border-radius: 0 0 var(--goa-temporary-notification-progress-bar-borderRadius, 0) var(--goa-temporary-notification-progress-bar-borderRadius, 0);
+ }
+
+ progress::-moz-progress-bar {
+ background-color: var(--goa-temporary-notification-progress-bar-color-fill, white);
+ border-radius: 0 0 var(--goa-temporary-notification-progress-bar-borderRadius, 0) var(--goa-temporary-notification-progress-bar-borderRadius, 0);
}
.show {
@@ -143,23 +182,28 @@
}
.hide.animate-up {
- transform: translateY(-100px);
+ transform: translateY(calc(-1 * var(--goa-temporary-notification-animation-distance, 100px)));
}
.hide.animate-down {
- transform: translateY(100px);
+ transform: translateY(var(--goa-temporary-notification-animation-distance, 100px));
}
.snackbar.success {
- background: var(--goa-color-success-default);
+ background: var(--goa-temporary-notification-color-bg-success, var(--goa-color-success-default));
}
.snackbar.failure {
- background: var(--goa-color-emergency-default);
+ background: var(--goa-temporary-notification-color-bg-failure, var(--goa-color-emergency-default));
}
.message {
flex: 1 1 auto;
- font: var(--goa-typography-body-m);
+ font: var(--goa-temporary-notification-typography, var(--goa-typography-body-m));
+ }
+
+ /* Add top margin to message when icon is present to vertically center first line with icon */
+ .content:has(goa-icon) .message {
+ margin-top: var(--goa-temporary-notification-padding-text-top, var(--goa-space-2xs));
}
From 73057cd5e6d823005493eb0d8c166a1812c21f75 Mon Sep 17 00:00:00 2001
From: syedszeeshan <47701214+syedszeeshan@users.noreply.github.com>
Date: Wed, 8 Oct 2025 19:46:40 -0400
Subject: [PATCH 16/80] feat(#2361): increase clickable area for radio and
checkbox
---
.../specs/checkbox.browser.spec.tsx | 48 +++++++++++++++
.../specs/radio.browser.spec.tsx | 59 +++++++++++++++++++
.../src/components/checkbox/Checkbox.svelte | 24 ++++++--
.../components/radio-item/RadioItem.svelte | 11 ++++
4 files changed, 136 insertions(+), 6 deletions(-)
diff --git a/libs/react-components/specs/checkbox.browser.spec.tsx b/libs/react-components/specs/checkbox.browser.spec.tsx
index 72d7e3fc0a..9cf69a6e33 100644
--- a/libs/react-components/specs/checkbox.browser.spec.tsx
+++ b/libs/react-components/specs/checkbox.browser.spec.tsx
@@ -84,4 +84,52 @@ describe("Checkbox", () => {
expect(childValue.element().textContent).toBe("false");
});
});
+
+ it("should have a 44px x 44px touch target area", async () => {
+ const result = render(
+
+ );
+
+ const checkbox = result.getByTestId("test-checkbox");
+ await vi.waitFor(() => {
+ expect(checkbox.element()).toBeTruthy();
+ });
+
+ const container = checkbox.element().querySelector(".container") as HTMLElement;
+ expect(container).toBeTruthy();
+
+ // Get computed styles for the ::before pseudo-element (touch target)
+ const beforeStyles = window.getComputedStyle(container, "::before");
+
+ // Verify the touch target dimensions
+ expect(beforeStyles.width).toBe("44px");
+ expect(beforeStyles.height).toBe("44px");
+ expect(beforeStyles.position).toBe("absolute");
+
+ // Verify the container itself has position: relative for proper positioning context
+ const containerStyles = window.getComputedStyle(container);
+ expect(containerStyles.position).toBe("relative");
+
+ // Verify the actual visual size of the container (24px) vs touch target (44px)
+ const containerRect = container.getBoundingClientRect();
+ expect(containerRect.width).toBe(24); // Visual checkbox is 24px
+ expect(containerRect.height).toBe(24); // Visual checkbox is 24px
+
+ // Verify the transform is applied correctly for centering
+ // CSS: transform: translate(-50%, -50%) converts to matrix(a, b, c, d, tx, ty)
+ // a,b,c,d: 2x2 transformation identity matrix
+ expect(beforeStyles.transform).toBe("matrix(1, 0, 0, 1, -22, -22)");
+
+ // Final verification: Check that all styles are applied and rendered
+ // After the page is fully loaded and all CSS is computed
+ await vi.waitFor(() => {
+ const finalContainerStyles = window.getComputedStyle(container);
+ const finalBeforeStyles = window.getComputedStyle(container, "::before");
+
+ // Verify final computed styles match expectations
+ expect(finalContainerStyles.position).toBe("relative");
+ expect(finalBeforeStyles.width).toBe("44px");
+ expect(finalBeforeStyles.height).toBe("44px");
+ });
+ });
});
diff --git a/libs/react-components/specs/radio.browser.spec.tsx b/libs/react-components/specs/radio.browser.spec.tsx
index 5d15c59caa..959fd13360 100644
--- a/libs/react-components/specs/radio.browser.spec.tsx
+++ b/libs/react-components/specs/radio.browser.spec.tsx
@@ -98,4 +98,63 @@ describe("Radio", () => {
expect(selectedValue.element().textContent).toBe("apple");
});
});
+
+ it("should have a 44px x 44px touch target area", async () => {
+ const result = render(
+
+
+
+ );
+
+ const radioInput = result.getByTestId("radio-option-option1");
+ await vi.waitFor(() => {
+ expect(radioInput.element()).toBeTruthy();
+ });
+
+ // Get the parent label element and find the .icon element
+ const label = radioInput.element().closest("label");
+ expect(label).toBeTruthy();
+
+ const icon = label?.querySelector(".icon") as HTMLElement;
+ expect(icon).toBeTruthy();
+
+ // Get computed styles for the ::before pseudo-element (touch target)
+ const beforeStyles = window.getComputedStyle(icon, "::before");
+
+ // Verify the touch target dimensions
+ expect(beforeStyles.width).toBe("44px");
+ expect(beforeStyles.height).toBe("44px");
+ expect(beforeStyles.position).toBe("absolute");
+
+ // Verify the icon itself has position: relative for proper positioning context
+ const iconStyles = window.getComputedStyle(icon);
+ expect(iconStyles.position).toBe("relative");
+
+ // Verify the actual visual size of the icon (24px) vs touch target (44px)
+ const iconRect = icon.getBoundingClientRect();
+ expect(iconRect.width).toBe(24); // Visual icon is 24px
+ expect(iconRect.height).toBe(24); // Visual icon is 24px
+
+ // Verify the transform is applied correctly for centering
+ // CSS: transform: translate(-50%, -50%) converts to matrix(a, b, c, d, tx, ty)
+ // a,b,c,d: 2x2 transformation identity matrix
+ expect(beforeStyles.transform).toBe("matrix(1, 0, 0, 1, -22, -22)");
+
+ // Check ::after pseudo-element (should not interfere with touch target)
+ const afterStyles = window.getComputedStyle(icon, "::after");
+ // ::after should not have conflicting dimensions or positioning
+ expect(afterStyles.position).not.toBe("absolute");
+
+ // Final verification: Check that all styles are applied and rendered
+ // After the page is fully loaded and all CSS is computed
+ await vi.waitFor(() => {
+ const finalIconStyles = window.getComputedStyle(icon);
+ const finalBeforeStyles = window.getComputedStyle(icon, "::before");
+
+ // Verify final computed styles match expectations
+ expect(finalIconStyles.position).toBe("relative");
+ expect(finalBeforeStyles.width).toBe("44px");
+ expect(finalBeforeStyles.height).toBe("44px");
+ });
+ });
});
diff --git a/libs/web-components/src/components/checkbox/Checkbox.svelte b/libs/web-components/src/components/checkbox/Checkbox.svelte
index 87b3224196..568f102847 100644
--- a/libs/web-components/src/components/checkbox/Checkbox.svelte
+++ b/libs/web-components/src/components/checkbox/Checkbox.svelte
@@ -165,12 +165,12 @@
const checkboxEl = (_rootEl?.getRootNode() as ShadowRoot)?.host as HTMLElement;
const fromCheckboxList = checkboxEl?.closest("goa-checkbox-list") !== null;
- relay
(
- _rootEl,
- FormFieldMountMsg,
- { name, el: _rootEl },
- { bubbles: !fromCheckboxList, timeout: 10 },
- );
+ relay(
+ _rootEl,
+ FormFieldMountMsg,
+ { name, el: _rootEl },
+ { bubbles: !fromCheckboxList, timeout: 10 },
+ );
}
function onChange(e: Event) {
@@ -387,6 +387,7 @@ max-width: ${maxwidth};
/* Container */
.container {
+ position: relative;
box-sizing: border-box;
border: var(--goa-checkbox-border);
border-radius: var(--goa-checkbox-border-radius);
@@ -398,6 +399,17 @@ max-width: ${maxwidth};
justify-content: center;
flex: 0 0 auto; /* prevent squishing of checkbox */
}
+
+ .container::before {
+ content: '';
+ position: absolute;
+ width: 44px;
+ height: 44px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
.container:hover {
border: var(--goa-checkbox-border-hover);
}
diff --git a/libs/web-components/src/components/radio-item/RadioItem.svelte b/libs/web-components/src/components/radio-item/RadioItem.svelte
index a48bc88f2b..f9ef00972f 100644
--- a/libs/web-components/src/components/radio-item/RadioItem.svelte
+++ b/libs/web-components/src/components/radio-item/RadioItem.svelte
@@ -342,6 +342,7 @@
}
.icon {
+ position: relative;
display: inline-block;
height: var(--goa-radio-size);
width: var(--goa-radio-size);
@@ -354,6 +355,16 @@
margin-top: var(--font-valign-fix);
}
+ .icon::before {
+ content: '';
+ position: absolute;
+ width: 44px;
+ height: 44px;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
.radio--disabled .label,
.radio--disabled ~ .description {
color: var(--goa-radio-label-color-disabled);
From dff763d9ade3ed4c0ca9fe1d21c90661fa2053e0 Mon Sep 17 00:00:00 2001
From: Benji Franck
Date: Fri, 21 Nov 2025 09:42:25 -0700
Subject: [PATCH 17/80] feat(#3142): update dropdown to v2
---
.../src/components/dropdown/Dropdown.svelte | 56 +++++++++++++++----
1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/libs/web-components/src/components/dropdown/Dropdown.svelte b/libs/web-components/src/components/dropdown/Dropdown.svelte
index af82eb7652..047ddd1cb5 100644
--- a/libs/web-components/src/components/dropdown/Dropdown.svelte
+++ b/libs/web-components/src/components/dropdown/Dropdown.svelte
@@ -66,6 +66,9 @@
export let error: string = "false";
export let multiselect: string = "false";
export let native: string = "false";
+ export let size: "default" | "compact" = "default";
+ export let version: "1" | "2" = "1";
+
/***
* @deprecated This property has no effect and will be removed in a future version
*/
@@ -754,6 +757,8 @@
data-testid={testid || `${name}-dropdown`}
class="dropdown"
class:dropdown-native={_native}
+ class:compact={size === "compact"}
+ class:v2={version === "2"}
style={`
${calculateMargin(mt, mr, mb, ml)};
--width: ${_width};
@@ -801,6 +806,7 @@
{/if}
@@ -827,7 +833,7 @@
aria-haspopup="listbox"
disabled={_disabled}
readonly={!_filterable}
- {placeholder}
+ placeholder={placeholder || (version === "2" ? "—Select—" : "")}
{name}
on:keydown={onInputKeyDown}
on:keyup={onInputKeyUp}
@@ -845,7 +851,8 @@
on:keydown={onClearIconKeyDown}
class="dropdown-icon--clear"
class:disabled={_disabled}
- size="medium"
+ disabled={_disabled ? "true" : "false"}
+ size={size === "compact" ? "xsmall" : "medium"}
theme="filled"
variant="dark"
icon="close"
@@ -856,7 +863,7 @@
testid="chevron"
id={name}
class="dropdown-icon--arrow"
- size="medium"
+ size={size === "compact" ? "small" : "medium"}
type={_isMenuVisible ? "chevron-up" : "chevron-down"}
/>
{/if}
@@ -936,20 +943,28 @@
cursor: pointer;
width: 100%;
}
+
.dropdown-input-group:hover {
box-shadow: var(--goa-dropdown-border-hover);
border: none;
}
- .dropdown-input-group:has(input:focus-visible) {
+
+ .dropdown-input-group:has(input:focus-visible),
+ .dropdown-input-group.error:has(:focus-visible) {
box-shadow: var(--goa-dropdown-border), var(--goa-dropdown-border-focus);
}
+
+ /* V2: Focus state has a single border */
+ .v2 .dropdown-input-group:has(input:focus-visible),
+ .v2 .dropdown-input-group.error:has(:focus-visible) {
+ box-shadow: var(--goa-dropdown-border-focus);
+ }
+
.dropdown-input-group.error,
.dropdown-input-group.error:hover {
box-shadow: var(--goa-dropdown-border-error);
}
- .dropdown-input-group.error:has(:focus-visible) {
- box-shadow: var(--goa-dropdown-border), var(--goa-dropdown-border-focus);
- }
+
@container not (--mobile) {
.dropdown-input-group {
width: var(--width, 100%);
@@ -1015,9 +1030,9 @@
/** menu **/
ul[role="listbox"] {
- border-radius: var(--goa-dropdown-border-radius);
+ border-radius: var(--goa-dropdown-menu-border-radius, var(--goa-dropdown-border-radius));
padding: 0;
- margin: 0;
+ margin: var(--goa-dropdown-menu-margin, 0);
}
/* dropdown items */
@@ -1031,8 +1046,10 @@
white-space: normal; /* Allows text to wrap */
word-break: break-word; /* Ensures long words break onto the next line */
overflow-wrap: break-word; /* Alternative for word wrapping */
+ border-radius: var(--goa-dropdown-item-border-radius, 0);
}
+
.dropdown-item:hover,
.dropdown-item--highlighted {
background: var(--goa-dropdown-item-color-bg-hover);
@@ -1088,8 +1105,9 @@
.dropdown-native::after {
content: "";
position: absolute;
- right: 0.6rem;
- top: 0.6rem;
+ right: var(--goa-dropdown-space-icon-text);
+ top: 50%;
+ transform: translateY(-50%);
pointer-events: none;
width: 1.5rem;
height: 1.5rem;
@@ -1109,4 +1127,20 @@
color: var(--goa-dropdown-color-text-placeholder);
opacity: 1;
}
+
+ input:disabled::placeholder {
+ color: var(--goa-dropdown-color-text-disabled);
+ }
+
+ /* Compact Size */
+ .compact input,
+ .compact select {
+ padding: var(--goa-dropdown-compact-padding);
+ height: var(--goa-dropdown-compact-height);
+ font: var(--goa-dropdown-compact-typography);
+ }
+
+ .compact .dropdown-item {
+ font: var(--goa-dropdown-compact-item-typography);
+ }
From b4bd11caade71166a1df5ab4e36451234916f477 Mon Sep 17 00:00:00 2001
From: Benji Franck
Date: Tue, 18 Nov 2025 16:00:05 -0700
Subject: [PATCH 18/80] feat(#3070): v2 badge with emphasis and size
---
.../src/components/badge/Badge.svelte | 184 +++++++++++++++++-
1 file changed, 179 insertions(+), 5 deletions(-)
diff --git a/libs/web-components/src/components/badge/Badge.svelte b/libs/web-components/src/components/badge/Badge.svelte
index ed0ac5d79f..6569984ef9 100644
--- a/libs/web-components/src/components/badge/Badge.svelte
+++ b/libs/web-components/src/components/badge/Badge.svelte
@@ -8,7 +8,8 @@
import { typeValidator, toBoolean } from "../../common/utils";
import type { GoAIconType } from "../icon/Icon.svelte";
- // Validator
+
+ // Validators
const [Types, validateType] = typeValidator(
"Badge type",
[
@@ -39,12 +40,36 @@
"red-light",
"violet-light",
"yellow-light",
+ "sky",
+ "prairie",
+ "lilac",
+ "pasture",
+ "sunset",
+ "dawn",
+ "default",
],
true,
);
+ const [badgeSizes, validateBadgeSize] = typeValidator("Badge size", [
+ "medium",
+ "large",
+ ]);
+
+ const [versions, validateVersion] = typeValidator("Badge version", [
+ "1",
+ "2",
+ ]);
+
+ const [emphasisLevels, validateEmphasisLevel] = typeValidator(
+ "Badge emphasis level",
+ ["subtle", "strong"],
+ );
+
//Type
type BadgeType = (typeof Types)[number];
+ type BadgeSize = (typeof badgeSizes)[number];
+ type BadgeVersion = (typeof versions)[number];
export let type: BadgeType;
@@ -54,6 +79,9 @@
export let icon: string = "";
export let icontype: GoAIconType | null = null;
export let arialabel: string = "";
+ export let size: BadgeSize = "medium";
+ export let emphasis: (typeof emphasisLevels)[number] = "strong";
+ export let version: BadgeVersion = "1";
// margin
export let mt: Spacing = null;
@@ -94,10 +122,20 @@
"red-light": "information-circle",
"violet-light": "information-circle",
"yellow-light": "information-circle",
+ sky: "information-circle",
+ prairie: "information-circle",
+ lilac: "information-circle",
+ pasture: "information-circle",
+ sunset: "information-circle",
+ dawn: "information-circle",
+ default: "information-circle",
}[type];
onMount(() => {
validateType(type);
+ validateBadgeSize(size);
+ validateEmphasisLevel(emphasis);
+ validateVersion(version);
if (!showIcon && !content) {
console.warn(
@@ -115,15 +153,16 @@
style={calculateMargin(mt, mr, mb, ml)}
data-testid={testid}
data-type="goa-badge"
- class="goa-badge badge-{type}"
+ class="goa-badge badge-{type} badge-{size} badge-{emphasis}"
class:icon-only={showIconOnly}
+ class:v2={version === "2"}
>
{#if showIcon}
{:else}
@@ -207,8 +246,14 @@
}
.goa-badge.badge-archived {
- background-color: var(--goa-color-greyscale-700);
- color: var(--goa-badge-dark-color-content);
+ background-color: var(
+ --goa-badge-archived-color-bg,
+ var(--goa-color-greyscale-700)
+ );
+ color: var(
+ --goa-badge-archived-color-content,
+ var(--goa-badge-dark-color-content)
+ );
}
.goa-badge.badge-aqua {
@@ -305,4 +350,133 @@
background-color: var(--goa-color-extended-light-yellow);
color: var(--goa-badge-light-color-content);
}
+
+ .v2 .goa-badge-content {
+ padding-bottom: 0;
+ }
+
+ /* Version 2: Default Colours */
+ .v2.badge-default {
+ background-color: var(--goa-badge-default-color-bg);
+ color: var(--goa-badge-default-color-content);
+ }
+
+ .v2.badge-default.badge-subtle {
+ background-color: var(--goa-badge-default-subtle-color-bg);
+ box-shadow: var(--goa-badge-default-subtle-border);
+ color: var(--goa-badge-default-subtle-color-content);
+ }
+
+ .v2.goa-badge.badge-archived.badge-subtle {
+ background-color: var(--goa-badge-archived-subtle-color-bg);
+ box-shadow: var(--goa-badge-archived-subtle-border);
+ color: var(--goa-badge-archived-subtle-color-content);
+ }
+
+ /* Version 2: Extended Colours */
+
+ .v2.badge-sky {
+ background-color: var(--goa-color-extended-sky-default);
+ color: var(--goa-color-extended-sky-text);
+ }
+
+ .v2.badge-prairie {
+ background-color: var(--goa-color-extended-prairie-default);
+ color: var(--goa-color-extended-prairie-text);
+ }
+
+ .v2.badge-lilac {
+ background-color: var(--goa-color-extended-lilac-default);
+ color: var(--goa-color-extended-lilac-text);
+ }
+
+ .v2.badge-pasture {
+ background-color: var(--goa-color-extended-pasture-default);
+ color: var(--goa-color-extended-pasture-text);
+ }
+
+ .v2.badge-sunset {
+ background-color: var(--goa-color-extended-sunset-default);
+ color: var(--goa-color-extended-sunset-text);
+ }
+
+ .v2.badge-dawn {
+ background-color: var(--goa-color-extended-dawn-default);
+ color: var(--goa-color-extended-dawn-text);
+ }
+
+ .v2.badge-subtle.badge-sky {
+ background-color: var(--goa-color-extended-sky-light);
+ box-shadow: var(--goa-color-extended-sky-subtle-border);
+ color: var(--goa-color-extended-sky-text);
+ }
+
+ .v2.badge-subtle.badge-prairie {
+ background-color: var(--goa-color-extended-prairie-light);
+ box-shadow: var(--goa-color-extended-prairie-subtle-border);
+ color: var(--goa-color-extended-prairie-text);
+ }
+
+ .v2.badge-subtle.badge-lilac {
+ background-color: var(--goa-color-extended-lilac-light);
+ box-shadow: var(--goa-color-extended-lilac-subtle-border);
+ color: var(--goa-color-extended-lilac-text);
+ }
+
+ .v2.badge-subtle.badge-pasture {
+ background-color: var(--goa-color-extended-pasture-light);
+ box-shadow: var(--goa-color-extended-pasture-subtle-border);
+ color: var(--goa-color-extended-pasture-text);
+ }
+
+ .v2.badge-subtle.badge-sunset {
+ background-color: var(--goa-color-extended-sunset-light);
+ box-shadow: var(--goa-color-extended-sunset-subtle-border);
+ color: var(--goa-color-extended-sunset-text);
+ }
+
+ .v2.badge-subtle.badge-dawn {
+ background-color: var(--goa-color-extended-dawn-light);
+ box-shadow: var(--goa-color-extended-dawn-subtle-border);
+ color: var(--goa-color-extended-dawn-text);
+ }
+
+ /* Version 2: Subtle emphasis for standard colours */
+
+ .v2.goa-badge.badge-subtle.badge-information {
+ background-color: var(--goa-badge-info-subtle-color-bg);
+ color: var(--goa-badge-info-subtle-color-content);
+ box-shadow: var(--goa-badge-info-subtle-border);
+ }
+
+ .v2.goa-badge.badge-subtle.badge-success {
+ background-color: var(--goa-badge-success-subtle-color-bg);
+ color: var(--goa-badge-success-subtle-color-content);
+ box-shadow: var(--goa-badge-success-subtle-border);
+ }
+
+ .v2.goa-badge.badge-subtle.badge-important {
+ background-color: var(--goa-badge-important-subtle-color-bg);
+ color: var(--goa-badge-important-subtle-color-content);
+ box-shadow: var(--goa-badge-important-subtle-border);
+ }
+
+ .v2.goa-badge.badge-subtle.badge-emergency {
+ background-color: var(--goa-badge-emergency-subtle-color-bg);
+ color: var(--goa-badge-emergency-subtle-color-content);
+ box-shadow: var(--goa-badge-emergency-subtle-border);
+ }
+
+ /* Version 2: Large size */
+
+ .v2.goa-badge.badge-large {
+ height: var(--goa-badge-height-large);
+ padding: var(--goa-badge-padding-large);
+ --goa-icon-size: var(--goa-badge-icon-size-large);
+ }
+
+ .v2.goa-badge.badge-large .goa-badge-content {
+ font-size: var(--goa-badge-font-size-large);
+ line-height: var(--goa-badge-line-height-large);
+ }
From 4544049cd25530e56765aead389fd045617178a0 Mon Sep 17 00:00:00 2001
From: Benji Franck
Date: Thu, 20 Nov 2025 08:05:22 -0700
Subject: [PATCH 19/80] feat(#2915): callout v2 layout and emphasis levels
---
.../src/components/callout/Callout.svelte | 210 ++++++++++++++++--
1 file changed, 196 insertions(+), 14 deletions(-)
diff --git a/libs/web-components/src/components/callout/Callout.svelte b/libs/web-components/src/components/callout/Callout.svelte
index 8784830b77..394226a1c4 100644
--- a/libs/web-components/src/components/callout/Callout.svelte
+++ b/libs/web-components/src/components/callout/Callout.svelte
@@ -19,16 +19,23 @@
"medium",
"large",
]);
+ const [CalloutEmphasis, validateCalloutEmphasis] = typeValidator(
+ "Callout emphasis",
+ ["high", "medium", "low"],
+ );
const [AriaLive, validateAriaLive] = typeValidator("Aria live", [
"off",
"assertive",
"polite",
]);
+ const [Version, validateVersion] = typeValidator("Version", ["1", "2"]);
// Types
type CalloutType = (typeof Types)[number];
type CalloutSize = (typeof CalloutSizes)[number];
+ type CalloutEmphasisType = (typeof CalloutEmphasis)[number];
type AriaLiveType = (typeof AriaLive)[number];
+ type VersionType = (typeof Version)[number];
// margin
export let mt: Spacing = null;
@@ -38,11 +45,13 @@
export let size: CalloutSize = "large";
export let type: CalloutType;
+ export let emphasis: CalloutEmphasisType = "medium";
export let heading: string = "";
export let maxwidth: string = "none";
export let testid: string = "";
export let arialive: AriaLiveType = "off";
export let icontheme: IconTheme = "outline";
+ export let version: VersionType = "1";
// Private
@@ -68,7 +77,9 @@
onMount(() => {
validateCalloutSize(size);
+ validateCalloutEmphasis(emphasis);
validateAriaLive(arialive);
+ validateVersion(version);
setTimeout(() => {
validateType(type);
@@ -85,24 +96,35 @@
${calculateMargin(mt, mr, mb, ml)};
max-width: ${maxwidth};
`}
- class="notification {type}"
+ class="notification {type} emphasis-{emphasis}"
class:medium={isMediumCallout}
+ class:v2={version === "2"}
data-testid={testid}
aria-live={arialive}
>
-
-
-
-
- {#if heading}
- {heading}
- {/if}
-
-
+ {#if version === "2"}
+
+
+
{heading}
+
+
+
+
+ {:else}
+
+
+
+
+ {#if heading}
+ {heading}
+ {/if}
+
+
+ {/if}
@@ -221,4 +243,164 @@
.notification.medium .icon {
padding: var(--goa-callout-m-statusbar-padding);
}
+
+ /* Version two: Layout */
+
+ .v2.notification {
+ flex-direction: column;
+ border: var(--goa-callout-border);
+ }
+
+ .v2.notification .heading {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ padding: var(--goa-callout-heading-padding);
+ gap: var(--goa-callout-heading-gap);
+ color: var(--goa-callout-heading-color);
+ }
+
+ .v2.notification .heading-label {
+ margin-top: var(--goa-space-3xs);
+ margin-bottom: var(--goa-space-3xs);
+ font: var(--goa-callout-heading-typography);
+ }
+
+ .v2.notification .body {
+ padding: var(--goa-callout-body-padding);
+ color: var(--goa-callout-body-color);
+ font: var(--goa-callout-body-typography);
+ }
+
+ /* Version two: Low emphasis layout */
+
+ .v2.emphasis-low .body {
+ padding: var(--goa-callout-l-with-heading-body-padding);
+ }
+
+ .v2.emphasis-low:has(.heading-label:empty) {
+ flex-direction: row;
+ align-items: start;
+ }
+
+ .v2.emphasis-low .heading-label:empty {
+ display: none;
+ }
+
+ .v2.emphasis-low:has(.heading-label:empty) .heading {
+ padding-right: var(--goa-space-xs);
+ }
+
+ .v2.emphasis-low:has(.heading-label:empty) .body {
+ padding: var(--goa-callout-l-without-heading-body-padding);
+ }
+
+ .v2.information {
+ background-color: var(--goa-callout-info-content-bg-color);
+ }
+
+ /* Version two: Types */
+
+ .v2.information .heading {
+ background-color: var(--goa-callout-info-heading-bg-color);
+ --fill-color: var(--goa-callout-info-icon-color);
+ }
+
+ .v2.information.emphasis-low {
+ border-color: var(--goa-callout-l-info-border-color);
+ background-color: var(--goa-callout-l-info-content-bg-color);
+ }
+
+ .v2.information.emphasis-high {
+ border-color: var(--goa-callout-h-info-border-color);
+ background-color: var(--goa-callout-h-info-content-bg-color);
+ }
+
+ .v2.information.emphasis-high .heading {
+ background-color: var(--goa-callout-h-info-heading-bg-color);
+ color: var(--goa-callout-h-info-heading-color);
+ --fill-color: var(--goa-callout-h-info-icon-color);
+ }
+
+ .v2.emergency {
+ background-color: var(--goa-callout-emergency-content-bg-color);
+ }
+
+ .v2.emergency .heading {
+ background-color: var(--goa-callout-emergency-heading-bg-color);
+ --fill-color: var(--goa-callout-emergency-icon-color);
+ }
+
+ .v2.emergency.emphasis-low {
+ border-color: var(--goa-callout-l-emergency-border-color);
+ background-color: var(--goa-callout-l-emergency-content-bg-color);
+ }
+
+ .v2.emergency.emphasis-high {
+ border-color: var(--goa-callout-h-emergency-border-color);
+ background-color: var(--goa-callout-h-emergency-content-bg-color);
+ }
+
+ .v2.emergency.emphasis-high .heading {
+ background-color: var(--goa-callout-h-emergency-heading-bg-color);
+ color: var(--goa-callout-h-emergency-heading-color);
+ --fill-color: var(--goa-callout-h-emergency-icon-color);
+ }
+
+ .v2.important {
+ background-color: var(--goa-callout-important-content-bg-color);
+ }
+
+ .v2.important .heading {
+ background-color: var(--goa-callout-important-heading-bg-color);
+ --fill-color: var(--goa-callout-important-icon-color);
+ }
+
+ .v2.important.emphasis-low {
+ border-color: var(--goa-callout-l-important-border-color);
+ background-color: var(--goa-callout-l-important-content-bg-color);
+ }
+
+ .v2.important.emphasis-high {
+ border-color: var(--goa-callout-h-important-border-color);
+ background-color: var(--goa-callout-h-important-content-bg-color);
+ }
+
+ .v2.important.emphasis-high .heading {
+ background-color: var(--goa-callout-h-important-heading-bg-color);
+ color: var(--goa-callout-h-important-heading-color);
+ --fill-color: var(--goa-callout-h-important-icon-color);
+ }
+
+ .v2.success {
+ background-color: var(--goa-callout-success-content-bg-color);
+ }
+
+ .v2.success .heading {
+ background-color: var(--goa-callout-success-heading-bg-color);
+ --fill-color: var(--goa-callout-success-icon-color);
+ }
+
+ .v2.success.emphasis-low {
+ border-color: var(--goa-callout-l-success-border-color);
+ background-color: var(--goa-callout-l-success-content-bg-color);
+ }
+
+ .v2.success.emphasis-high {
+ border-color: var(--goa-callout-h-success-border-color);
+ background-color: var(--goa-callout-h-success-content-bg-color);
+ }
+
+ .v2.success.emphasis-high .heading {
+ background-color: var(--goa-callout-h-success-heading-bg-color);
+ color: var(--goa-callout-h-success-heading-color);
+ --fill-color: var(--goa-callout-h-success-icon-color);
+ }
+
+ .v2.information.emphasis-low .heading,
+ .v2.important.emphasis-low .heading,
+ .v2.emergency.emphasis-low .heading,
+ .v2.success.emphasis-low .heading {
+ background-color: transparent;
+ }
From c4e0ef17de9817c209531a2ae15f15c0bc7383a1 Mon Sep 17 00:00:00 2001
From: Chris Olsen