{label}
diff --git a/src/internal/components/abstract-switch/styles.scss b/src/internal/components/abstract-switch/styles.scss
index be96eda344..6cb3f35076 100644
--- a/src/internal/components/abstract-switch/styles.scss
+++ b/src/internal/components/abstract-switch/styles.scss
@@ -13,8 +13,13 @@
display: block;
}
+/* stylelint-disable custom-property-pattern */
+
.label {
- color: awsui.$color-text-form-default;
+ color: var(--awsui-style-color-text-label, #{awsui.$color-text-form-default});
+ @include styles.with-motion {
+ transition: color var(--awsui-style-transition-duration, 0s) var(--awsui-style-transition-easing, ease);
+ }
}
.outline {
@@ -59,9 +64,12 @@
.label,
.description {
- padding-inline-start: awsui.$space-xs;
+ padding-inline-start: var(--awsui-style-space-control-label, #{awsui.$space-xs});
&-disabled {
- color: awsui.$color-text-control-disabled;
+ color: var(--awsui-style-color-text-label-disabled, #{awsui.$color-text-control-disabled});
+ }
+ &-readonly {
+ color: var(--awsui-style-color-text-label-readonly, #{awsui.$color-text-form-default});
}
}
diff --git a/src/internal/components/checkbox-icon/styles.scss b/src/internal/components/checkbox-icon/styles.scss
index 120f7a5bc3..f2b056aebb 100644
--- a/src/internal/components/checkbox-icon/styles.scss
+++ b/src/internal/components/checkbox-icon/styles.scss
@@ -6,6 +6,8 @@
@use '../../styles' as styles;
@use '../../styles/tokens' as awsui;
+/* stylelint-disable custom-property-pattern */
+
.root {
position: absolute;
inline-size: 100%;
@@ -14,35 +16,69 @@
inset-inline-start: 0;
> .styled-box {
- fill: awsui.$color-background-control-default;
- stroke: awsui.$color-border-control-default;
- stroke-width: awsui.$border-width-field;
+ fill: var(
+ --awsui-style-color-background-control-default,
+ var(--awsui-style-color-background-control, #{awsui.$color-background-control-default})
+ );
+ stroke: var(
+ --awsui-style-color-border-control-default,
+ var(--awsui-style-color-border-control, #{awsui.$color-border-control-default})
+ );
+ stroke-width: var(--awsui-style-border-width-field, #{awsui.$border-width-field});
+
@include styles.with-motion {
transition:
- fill awsui.$motion-duration-transition-quick awsui.$motion-easing-transition-quick,
- stroke awsui.$motion-duration-transition-quick awsui.$motion-easing-transition-quick;
+ fill var(--awsui-style-transition-duration, #{awsui.$motion-duration-transition-quick})
+ var(--awsui-style-transition-easing, #{awsui.$motion-easing-transition-quick}),
+ stroke var(--awsui-style-transition-duration, #{awsui.$motion-duration-transition-quick})
+ var(--awsui-style-transition-easing, #{awsui.$motion-easing-transition-quick});
}
+
&-checked,
&-indeterminate {
- fill: awsui.$color-background-control-checked;
- stroke: awsui.$color-border-control-checked;
+ fill: var(
+ --awsui-style-color-background-control-checked,
+ var(--awsui-style-color-background-control, #{awsui.$color-background-control-checked})
+ );
+ stroke: var(
+ --awsui-style-color-border-control-checked,
+ var(--awsui-style-color-border-control, #{awsui.$color-border-control-checked})
+ );
}
+
&-disabled,
&-readonly {
- fill: awsui.$color-background-control-disabled;
- stroke: awsui.$color-border-control-disabled;
+ fill: var(
+ --awsui-style-color-background-control-disabled,
+ var(--awsui-style-color-background-control, #{awsui.$color-background-control-disabled})
+ );
+ stroke: var(
+ --awsui-style-color-border-control-disabled,
+ var(--awsui-style-color-border-control, #{awsui.$color-border-control-disabled})
+ );
}
}
> .styled-line {
- stroke: awsui.$color-foreground-control-default;
- stroke-width: 2;
+ stroke: var(
+ --awsui-style-color-foreground-control-default,
+ var(--awsui-style-color-foreground-control, #{awsui.$color-foreground-control-default})
+ );
+ stroke-width: var(--awsui-style-stroke-width-control, 2);
fill: none;
+
&-disabled {
- stroke: awsui.$color-foreground-control-disabled;
+ stroke: var(
+ --awsui-style-color-foreground-control-disabled,
+ var(--awsui-style-color-foreground-control, #{awsui.$color-foreground-control-disabled})
+ );
}
+
&-readonly {
- stroke: awsui.$color-foreground-control-read-only;
+ stroke: var(
+ --awsui-style-color-foreground-control-readonly,
+ var(--awsui-style-color-foreground-control, #{awsui.$color-foreground-control-read-only})
+ );
}
}
}
diff --git a/src/internal/components/menu-dropdown/styles.scss b/src/internal/components/menu-dropdown/styles.scss
index 3847f51065..ef27c7fcc0 100644
--- a/src/internal/components/menu-dropdown/styles.scss
+++ b/src/internal/components/menu-dropdown/styles.scss
@@ -7,6 +7,8 @@
@use '../../styles/tokens' as awsui;
@use '@cloudscape-design/component-toolkit/internal/focus-visible' as focus-visible;
+/* stylelint-disable custom-property-pattern */
+
.button {
@include styles.styles-reset;
@include styles.text-wrapping;
@@ -25,21 +27,22 @@
border-block: transparent;
border-inline: transparent;
background: transparent;
- color: awsui.$color-text-interactive-default;
+ color: var(--awsui-style-menu-trigger-color, #{awsui.$color-text-interactive-default});
&:hover {
- color: awsui.$color-text-interactive-hover;
+ color: var(--awsui-style-menu-trigger-color-hover, #{awsui.$color-text-interactive-hover});
+ background: var(--awsui-style-menu-trigger-background-hover, transparent);
text-decoration: none;
}
&:active,
&.expanded {
- background: transparent;
- color: awsui.$color-text-interactive-active;
+ background: var(--awsui-style-menu-trigger-background-active, transparent);
+ color: var(--awsui-style-menu-trigger-color-active, #{awsui.$color-text-interactive-active});
}
&.expanded {
- color: awsui.$color-text-accent;
+ color: var(--awsui-style-menu-trigger-color-active, #{awsui.$color-text-accent});
}
&:focus {
diff --git a/src/internal/components/option/interfaces.ts b/src/internal/components/option/interfaces.ts
index 3050f4983d..3e489feb48 100644
--- a/src/internal/components/option/interfaces.ts
+++ b/src/internal/components/option/interfaces.ts
@@ -25,6 +25,10 @@ interface BaseOption {
export interface OptionDefinition extends BaseOption {
__labelPrefix?: string;
+ /**
+ * @deprecated Use the consuming component's `classNames.options` instead.
+ */
+ className?: string;
}
interface InternalOptionDefinition extends OptionDefinition {
diff --git a/src/internal/components/radio-button/styles.scss b/src/internal/components/radio-button/styles.scss
index f40700c53e..f5c5f30e2e 100644
--- a/src/internal/components/radio-button/styles.scss
+++ b/src/internal/components/radio-button/styles.scss
@@ -8,6 +8,8 @@
@use '../../styles/foundation' as foundation;
@use '../../generated/custom-css-properties/index.scss' as custom-props;
+/* stylelint-disable custom-property-pattern */
+
$radio-size: awsui.$size-control;
.radio-control {
@@ -16,29 +18,38 @@ $radio-size: awsui.$size-control;
.outline {
#{custom-props.$styleFocusRingBoxShadow}: 0 0 0
- var(#{custom-props.$styleFocusRingBorderWidth}, foundation.$box-shadow-focused-width)
- var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused);
+ var(
+ --awsui-style-focus-ring-border-width,
+ var(#{custom-props.$styleFocusRingBorderWidth}, foundation.$box-shadow-focused-width)
+ )
+ var(
+ --awsui-style-focus-ring-border-color,
+ var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused)
+ );
@include styles.focus-highlight(
$gutter: 2px,
- $border-radius: var(#{custom-props.$styleFocusRingBorderRadius}, awsui.$border-radius-control-circular-focus-ring),
+ $border-radius: var(
+ --awsui-style-focus-ring-border-radius,
+ var(#{custom-props.$styleFocusRingBorderRadius}, awsui.$border-radius-control-circular-focus-ring)
+ ),
$box-shadow: var(#{custom-props.$styleFocusRingBoxShadow})
);
}
.styled-circle-border {
- stroke: awsui.$color-border-control-default;
- fill: awsui.$color-background-control-default;
+ stroke: var(--awsui-style-color-border-control-default, #{awsui.$color-border-control-default});
+ fill: var(--awsui-style-color-background-control-default, #{awsui.$color-background-control-default});
&.styled-circle-disabled,
&.styled-circle-readonly {
- fill: awsui.$color-background-control-disabled;
- stroke: awsui.$color-background-control-disabled;
+ fill: var(--awsui-style-color-background-control-disabled, #{awsui.$color-background-control-disabled});
+ stroke: var(--awsui-style-color-border-control-disabled, #{awsui.$color-background-control-disabled});
}
}
.styled-circle-fill {
- stroke: awsui.$color-background-control-checked;
- fill: awsui.$color-foreground-control-default;
+ stroke: var(--awsui-style-color-background-control-checked, #{awsui.$color-background-control-checked});
+ fill: var(--awsui-style-color-foreground-control-default, #{awsui.$color-foreground-control-default});
opacity: 0;
@include styles.with-motion {
transition: opacity awsui.$motion-duration-transition-quick awsui.$motion-easing-transition-quick;
@@ -47,11 +58,11 @@ $radio-size: awsui.$size-control;
opacity: 1;
}
&.styled-circle-disabled {
- fill: awsui.$color-foreground-control-disabled;
- stroke: awsui.$color-background-control-disabled;
+ fill: var(--awsui-style-color-foreground-control-disabled, #{awsui.$color-foreground-control-disabled});
+ stroke: var(--awsui-style-color-background-control-disabled, #{awsui.$color-background-control-disabled});
}
&.styled-circle-readonly {
- fill: awsui.$color-foreground-control-read-only;
- stroke: awsui.$color-background-control-disabled;
+ fill: var(--awsui-style-color-foreground-control-readonly, #{awsui.$color-foreground-control-read-only});
+ stroke: var(--awsui-style-color-background-control-disabled, #{awsui.$color-background-control-disabled});
}
}
diff --git a/src/internal/components/selectable-item/index.tsx b/src/internal/components/selectable-item/index.tsx
index 707af46315..17fe40afe8 100644
--- a/src/internal/components/selectable-item/index.tsx
+++ b/src/internal/components/selectable-item/index.tsx
@@ -129,6 +129,7 @@ const SelectableItem = (
@@ -172,6 +173,7 @@ const InternalLink = React.forwardRef(
className: clsx(
styles.link,
baseProps.className,
+ classNames?.root,
applyButtonStyles ? styles.button : null,
styles[getVariantStyle(variant)],
styles[getFontSizeStyle(variant, fontSize)],
diff --git a/src/link/styles.scss b/src/link/styles.scss
index 5a327ce573..1f310c727a 100644
--- a/src/link/styles.scss
+++ b/src/link/styles.scss
@@ -11,14 +11,22 @@
@use '../internal/generated/custom-css-properties/index.scss' as custom-props;
@use '../internal/styles/foundation' as foundation;
+/* stylelint-disable custom-property-pattern */
+
.link {
@include styles.styles-reset;
display: inline;
white-space: inherit;
#{custom-props.$styleFocusRingBoxShadow}: 0 0 0
- var(#{custom-props.$styleFocusRingBorderWidth}, awsui.$border-link-focus-ring-shadow-spread)
- var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused);
+ var(
+ --awsui-style-link-focus-ring-border-width,
+ var(#{custom-props.$styleFocusRingBorderWidth}, awsui.$border-link-focus-ring-shadow-spread)
+ )
+ var(
+ --awsui-style-link-focus-ring-color,
+ var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused)
+ );
@include styles.link-default;
@@ -53,8 +61,14 @@
@include focus-visible.when-visible {
@include styles.link-focus(
- $border-color: var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused),
- $border-radius: var(#{custom-props.$styleFocusRingBorderRadius}, awsui.$border-radius-control-default-focus-ring),
+ $border-color: var(
+ --awsui-style-link-focus-ring-color,
+ var(#{custom-props.$styleFocusRingBorderColor}, awsui.$color-border-item-focused)
+ ),
+ $border-radius: var(
+ --awsui-style-link-focus-ring-border-radius,
+ var(#{custom-props.$styleFocusRingBorderRadius}, awsui.$border-radius-control-default-focus-ring)
+ ),
$box-shadow: var(#{custom-props.$styleFocusRingBoxShadow})
);
}
diff --git a/src/modal/interfaces.ts b/src/modal/interfaces.ts
index 44b216e321..2908929da9 100644
--- a/src/modal/interfaces.ts
+++ b/src/modal/interfaces.ts
@@ -104,12 +104,24 @@ export interface ModalProps extends BaseComponentProps, BaseModalProps {
* @analytics
*/
analyticsMetadata?: ModalProps.AnalyticsMetadata;
+
+ /**
+ * An object that maps the modal's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The modal's root element.
+ * @awsuiSystem core
+ */
+ classNames?: ModalProps.ClassNames;
}
export namespace ModalProps {
export type Size = 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' | 'max';
export type Position = 'center' | 'top';
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface DismissDetail {
reason: string;
}
diff --git a/src/modal/internal.tsx b/src/modal/internal.tsx
index 16761199a1..086c3ae86a 100644
--- a/src/modal/internal.tsx
+++ b/src/modal/internal.tsx
@@ -108,6 +108,7 @@ function PortaledModal({
__subStepRef,
__subStepFunnelProps,
referrerId,
+ classNames,
...rest
}: PortaledModalProps) {
const instanceUniqueId = useUniqueId();
@@ -246,6 +247,7 @@ function PortaledModal({
styles.root,
{ [styles.hidden]: !visible },
baseProps.className,
+ classNames?.root,
isRefresh && styles.refresh
)}
role="dialog"
diff --git a/src/modal/styles.scss b/src/modal/styles.scss
index 1c26f4b0be..0ec7f0f9a6 100644
--- a/src/modal/styles.scss
+++ b/src/modal/styles.scss
@@ -9,11 +9,13 @@
@use './motion';
@use '../internal/generated/custom-css-properties/index.scss' as custom-props;
+/* stylelint-disable custom-property-pattern */
+
$modal-z-index: 5000;
.root {
@include styles.styles-reset;
- background-color: awsui.$color-background-modal-overlay;
+ background-color: var(--awsui-style-modal-overlay-background, #{awsui.$color-background-modal-overlay});
display: flex;
align-items: center;
@@ -102,13 +104,13 @@ $modal-z-index: 5000;
.container {
@include styles.styles-reset;
display: block;
- background-color: awsui.$color-background-container-content;
+ background-color: var(--awsui-style-modal-background, #{awsui.$color-background-container-content});
word-wrap: break-word;
border-block-start: awsui.$border-container-top-width solid awsui.$color-border-container-top;
- border-start-start-radius: awsui.$border-radius-container;
- border-start-end-radius: awsui.$border-radius-container;
- border-end-start-radius: awsui.$border-radius-container;
- border-end-end-radius: awsui.$border-radius-container;
+ border-start-start-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
+ border-start-end-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
+ border-end-start-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
+ border-end-end-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
box-shadow: awsui.$shadow-modal;
&.custom-height-container {
@@ -139,10 +141,10 @@ $modal-z-index: 5000;
padding-block-start: awsui.$space-container-header-top;
padding-block-end: awsui.$space-container-header-bottom;
padding-inline: awsui.$space-modal-horizontal;
- background-color: awsui.$color-background-container-header;
+ background-color: var(--awsui-style-modal-header-background, #{awsui.$color-background-container-header});
border-block-end: 1px solid awsui.$color-border-container-divider;
- border-start-start-radius: awsui.$border-radius-container;
- border-start-end-radius: awsui.$border-radius-container;
+ border-start-start-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
+ border-start-end-radius: var(--awsui-style-modal-border-radius, #{awsui.$border-radius-container});
border-end-start-radius: 0;
border-end-end-radius: 0;
}
diff --git a/src/multiselect/interfaces.ts b/src/multiselect/interfaces.ts
index e031bfa715..2fa419aa29 100644
--- a/src/multiselect/interfaces.ts
+++ b/src/multiselect/interfaces.ts
@@ -80,6 +80,16 @@ export interface MultiselectProps extends BaseSelectProps {
* Specifies a render function to render custom options in the dropdown menu.
*/
renderOption?: MultiselectProps.MultiselectOptionItemRenderer;
+
+ /**
+ * An object that maps the multiselect's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The multiselect's root element.
+ * * `options` - Each option item. A string is applied to all; a function receiving
+ * `{ option }` enables per-option customization.
+ * @awsuiSystem core
+ */
+ classNames?: MultiselectProps.ClassNames;
}
export namespace MultiselectProps {
@@ -121,6 +131,11 @@ export namespace MultiselectProps {
filterText?: string;
}) => ReactNode | null;
+ export interface ClassNames {
+ root?: string;
+ options?: string | ((args: { option: MultiselectProps.Option }) => string | undefined);
+ }
+
export type DeselectAriaLabelFunction = (option: Option) => string;
export type TriggerVariant = 'placeholder' | 'tokens';
diff --git a/src/multiselect/internal.tsx b/src/multiselect/internal.tsx
index 28b50ce0e9..d8b99eaca6 100644
--- a/src/multiselect/internal.tsx
+++ b/src/multiselect/internal.tsx
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
-import React, { useRef, useState } from 'react';
+import React, { useMemo, useRef, useState } from 'react';
import clsx from 'clsx';
import { useResizeObserver, useUniqueId } from '@cloudscape-design/component-toolkit/internal';
@@ -10,6 +10,7 @@ import { useInternalI18n } from '../i18n/context';
import { getBaseProps } from '../internal/base-component';
import { getBreakpointValue } from '../internal/breakpoints';
import DropdownFooter from '../internal/components/dropdown-footer/index.js';
+import { isGroup } from '../internal/components/option/utils/filter-options';
import ScreenreaderOnly from '../internal/components/screenreader-only';
import { useFormFieldContext } from '../internal/context/form-field-context';
import { InternalBaseComponentProps } from '../internal/hooks/use-base-component/index.js';
@@ -63,6 +64,7 @@ const InternalMultiselect = React.forwardRef(
autoFocus,
enableSelectAll,
renderOption,
+ classNames,
...restProps
}: InternalMultiselectProps,
externalRef: React.Ref
@@ -71,6 +73,19 @@ const InternalMultiselect = React.forwardRef(
const formFieldContext = useFormFieldContext(restProps);
const i18n = useInternalI18n('multiselect');
+ const resolvedOptions = useMemo(() => {
+ if (!classNames?.options) {
+ return options;
+ }
+ const resolve = (option: MultiselectProps.Option): string | undefined =>
+ typeof classNames.options === 'function' ? classNames.options({ option }) : classNames.options;
+ return options.map(entry =>
+ isGroup(entry)
+ ? { ...entry, options: entry.options.map(opt => ({ ...opt, className: resolve(opt) ?? opt.className })) }
+ : { ...entry, className: resolve(entry) ?? entry.className }
+ );
+ }, [options, classNames]);
+
const selfControlId = useUniqueId('trigger');
const controlId = formFieldContext.controlId ?? selfControlId;
const ariaLabelId = useUniqueId('multiselect-ariaLabel-');
@@ -78,7 +93,7 @@ const InternalMultiselect = React.forwardRef(
const [filteringValue, setFilteringValue] = useState('');
const multiselectProps = useMultiselect({
- options,
+ options: resolvedOptions,
selectedOptions,
filteringType,
disabled,
@@ -167,7 +182,7 @@ const InternalMultiselect = React.forwardRef(
diff --git a/src/progress-bar/interfaces.ts b/src/progress-bar/interfaces.ts
index 72d1abf549..a66b892ba1 100644
--- a/src/progress-bar/interfaces.ts
+++ b/src/progress-bar/interfaces.ts
@@ -89,12 +89,24 @@ export interface ProgressBarProps extends BaseComponentProps {
* @awsuiSystem core
*/
style?: ProgressBarProps.Style;
+
+ /**
+ * An object that maps the progress bar's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The progress bar's root element.
+ * @awsuiSystem core
+ */
+ classNames?: ProgressBarProps.ClassNames;
}
export namespace ProgressBarProps {
export type Status = 'in-progress' | 'success' | 'error';
export type Variant = 'standalone' | 'flash' | 'key-value';
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface Style {
progressBar?: {
backgroundColor?: string;
diff --git a/src/progress-bar/styles.scss b/src/progress-bar/styles.scss
index bbb99c06ae..9e1d07f0e8 100644
--- a/src/progress-bar/styles.scss
+++ b/src/progress-bar/styles.scss
@@ -2,21 +2,28 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
+/* stylelint-disable custom-property-pattern */
@use '../internal/styles/tokens' as awsui;
@use '../internal/styles' as styles;
@use '../internal/generated/custom-css-properties/index.scss' as custom-props;
@use './motion';
-$progress-height: var(#{custom-props.$progressBarHeight}, 0.4 * styles.$base-size);
-$progress-border-radius: var(#{custom-props.$progressBarBorderRadius}, 10px);
+$progress-height: var(
+ --awsui-style-progress-bar-height,
+ var(#{custom-props.$progressBarHeight}, 0.4 * styles.$base-size)
+);
+$progress-border-radius: var(
+ --awsui-style-progress-bar-border-radius,
+ var(#{custom-props.$progressBarBorderRadius}, 10px)
+);
$progress-background-color: var(
- #{custom-props.$progressBarBackgroundColor},
- awsui.$color-background-progress-bar-default
+ --awsui-style-progress-bar-track-color,
+ var(#{custom-props.$progressBarBackgroundColor}, awsui.$color-background-progress-bar-default)
);
$progress-value-background-color: var(
- #{custom-props.$progressValueBackgroundColor},
- awsui.$color-background-progress-bar-value-default
+ --awsui-style-progress-bar-value-color,
+ var(#{custom-props.$progressValueBackgroundColor}, awsui.$color-background-progress-bar-value-default)
);
.root {
@@ -80,6 +87,9 @@ $progress-value-background-color: var(
.percentage {
/* used in test-utils */
+ color: var(--awsui-style-progress-bar-percentage-color, inherit);
+ font-size: var(--awsui-style-progress-bar-percentage-font-size, inherit);
+ font-weight: var(--awsui-style-progress-bar-percentage-font-weight, inherit);
}
@mixin general-progress-background-style {
diff --git a/src/prompt-input/interfaces.ts b/src/prompt-input/interfaces.ts
index 8dbc44fe52..ac7f57ae34 100644
--- a/src/prompt-input/interfaces.ts
+++ b/src/prompt-input/interfaces.ts
@@ -311,9 +311,21 @@ export interface PromptInputProps
* @awsuiSystem core
*/
style?: PromptInputProps.Style;
+
+ /**
+ * An object that maps the prompt input's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The prompt input's root element.
+ * @awsuiSystem core
+ */
+ classNames?: PromptInputProps.ClassNames;
}
export namespace PromptInputProps {
+ export interface ClassNames {
+ root?: string;
+ }
+
export type KeyDetail = BaseKeyDetail;
export interface I18nStrings {
diff --git a/src/prompt-input/internal.tsx b/src/prompt-input/internal.tsx
index 2e4ec2a0ee..900aa6826d 100644
--- a/src/prompt-input/internal.tsx
+++ b/src/prompt-input/internal.tsx
@@ -80,6 +80,7 @@ const InternalPromptInput = React.forwardRef(
onTriggerDetected,
i18nStrings,
__internalRootRef,
+ classNames,
...rest
}: InternalPromptInputProps,
ref: Ref
@@ -384,7 +385,7 @@ const InternalPromptInput = React.forwardRef(
+
{!renderResult && hasCheckbox && (
diff --git a/src/slider/interfaces.ts b/src/slider/interfaces.ts
index c2e328bb37..1bd4d31d95 100644
--- a/src/slider/interfaces.ts
+++ b/src/slider/interfaces.ts
@@ -89,9 +89,21 @@ export interface SliderProps extends BaseComponentProps, FormFieldValidationCont
* @awsuiSystem core
*/
style?: SliderProps.Style;
+
+ /**
+ * An object that maps the slider's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The slider's root element.
+ * @awsuiSystem core
+ */
+ classNames?: SliderProps.ClassNames;
}
export namespace SliderProps {
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface ChangeDetail {
value: number;
}
diff --git a/src/slider/internal.tsx b/src/slider/internal.tsx
index e1de93e7e9..5ed86dc42d 100644
--- a/src/slider/internal.tsx
+++ b/src/slider/internal.tsx
@@ -47,6 +47,7 @@ export default function InternalSlider({
valueFormatter,
i18nStrings,
style,
+ classNames,
__internalRootRef,
...rest
}: InternalSliderProps) {
@@ -132,7 +133,7 @@ export default function InternalSlider({
.header-cell-content:hover,
&.header-cell-sorted > .header-cell-content {
- color: awsui.$color-text-interactive-active;
+ color: var(--awsui-style-table-header-color, #{awsui.$color-text-interactive-active});
& > .sorting-icon {
- color: awsui.$color-text-interactive-active;
+ color: var(--awsui-style-table-sorting-icon-color, #{awsui.$color-text-interactive-active});
}
}
}
diff --git a/src/table/interfaces.tsx b/src/table/interfaces.tsx
index 1628cf5492..d3a7b698e2 100644
--- a/src/table/interfaces.tsx
+++ b/src/table/interfaces.tsx
@@ -144,6 +144,24 @@ export interface TableProps
extends BaseComponentProps {
*/
selectedItems?: ReadonlyArray;
+ /**
+ * Specifies a class name applied to each selection cell.
+ * Can be a string (applied to all) or a function receiving `{ item }` for row-level
+ * customization. When called for the header "select all" control, `item` is `undefined`.
+ * @deprecated Use `classNames.selection` instead.
+ */
+ selectionClassName?: string | ((props: { item: T | undefined }) => string);
+
+ /**
+ * An object that maps the table's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The table's root element.
+ * * `selection` - Each selection cell. A string is applied to all; a function receiving
+ * `{ item }` enables row-level customization (`item` is `undefined` for the header "select all" control).
+ * @awsuiSystem core
+ */
+ classNames?: TableProps.ClassNames;
+
/**
* Use this slot to add filtering controls to the table.
*/
@@ -432,6 +450,11 @@ export interface TableProps extends BaseComponentProps {
}
export namespace TableProps {
+ export interface ClassNames {
+ root?: string;
+ selection?: string | ((props: { item: T | undefined }) => string);
+ }
+
export interface AnalyticsMetadata {
instanceIdentifier?: string;
flowType?: 'view-resource';
diff --git a/src/table/internal.tsx b/src/table/internal.tsx
index cf2e4db610..4ea2588af0 100644
--- a/src/table/internal.tsx
+++ b/src/table/internal.tsx
@@ -113,6 +113,8 @@ const InternalTable = React.forwardRef(
selectionType: externalSelectionType,
selectedItems,
isItemDisabled,
+ selectionClassName: selectionClassNameProp,
+ classNames,
ariaLabels,
onSelectionChange,
onSortingChange,
@@ -159,6 +161,9 @@ const InternalTable = React.forwardRef(
const baseProps = getBaseProps(rest);
+ // `classNames.selection` supersedes the deprecated `selectionClassName` prop.
+ const selectionClassName = classNames?.selection ?? selectionClassNameProp;
+
const prevStickyHeader = usePrevious(stickyHeader);
if (prevStickyHeader !== undefined && !!stickyHeader !== !!prevStickyHeader) {
warnOnce(
@@ -393,6 +398,8 @@ const InternalTable = React.forwardRef(
const theadProps: TheadProps = {
selectionType,
getSelectAllProps: selection.getSelectAllProps,
+ selectionClassName:
+ typeof selectionClassName === 'function' ? selectionClassName({ item: undefined }) : selectionClassName,
columnDefinitions: visibleColumnDefinitions,
variant: computedVariant,
tableVariant: computedVariant,
@@ -465,7 +472,7 @@ const InternalTable = React.forwardRef(
{...baseProps}
{...tableInteractionAttributes}
__internalRootRef={__internalRootRef}
- className={clsx(baseProps.className, styles.root)}
+ className={clsx(baseProps.className, classNames?.root, styles.root)}
__funnelSubStepProps={__funnelSubStepProps}
__fullPage={variant === 'full-page'}
header={
@@ -647,6 +654,10 @@ const InternalTable = React.forwardRef(
onFocusUp: moveFocusUp,
rowIndex,
itemKey: rowId,
+ selectionClassName:
+ typeof selectionClassName === 'function'
+ ? selectionClassName({ item: row.item })
+ : selectionClassName,
}}
verticalAlign={cellVerticalAlign}
tableVariant={computedVariant}
@@ -744,7 +755,17 @@ const InternalTable = React.forwardRef(
columnId={selectionColumnId}
verticalAlign={cellVerticalAlign}
tableVariant={computedVariant}
- selectionControlProps={selectionType === 'group' ? loaderSelectionProps : undefined}
+ selectionControlProps={
+ selectionType === 'group' && loaderSelectionProps
+ ? {
+ ...loaderSelectionProps,
+ selectionClassName:
+ typeof selectionClassName === 'function'
+ ? selectionClassName({ item: row.item ?? undefined })
+ : selectionClassName,
+ }
+ : undefined
+ }
isSelected={selectionType === 'group' && !!loaderSelectionProps?.checked}
/>
) : null}
diff --git a/src/table/selection/selection-cell.tsx b/src/table/selection/selection-cell.tsx
index f2b894af81..4ca6e8648e 100644
--- a/src/table/selection/selection-cell.tsx
+++ b/src/table/selection/selection-cell.tsx
@@ -18,6 +18,7 @@ interface TableHeaderSelectionCellProps extends Omit ItemSelectionProps;
onFocusMove: ((sourceElement: HTMLElement, fromIndex: number, direction: -1 | 1) => void) | undefined;
+ selectionClassName?: string;
}
interface TableBodySelectionCellProps
@@ -30,6 +31,7 @@ export function TableHeaderSelectionCell({
singleSelectionHeaderAriaLabel,
getSelectAllProps,
onFocusMove,
+ selectionClassName,
...props
}: TableHeaderSelectionCellProps) {
const selectAllProps = getSelectAllProps ? getSelectAllProps() : undefined;
@@ -50,6 +52,7 @@ export function TableHeaderSelectionCell({
onFocusMove!(event.target as HTMLElement, -1, +1);
}}
focusedComponent={focusedComponent}
+ selectionClassName={selectionClassName}
{...selectAllProps}
{...(props.sticky ? { tabIndex: -1 } : {})}
/>
diff --git a/src/table/selection/selection-control.tsx b/src/table/selection/selection-control.tsx
index 7d6b7458f5..f91c86b1f4 100644
--- a/src/table/selection/selection-control.tsx
+++ b/src/table/selection/selection-control.tsx
@@ -23,6 +23,7 @@ export interface SelectionControlProps extends ItemSelectionProps {
rowIndex?: number;
itemKey?: string;
verticalAlign?: 'middle' | 'top';
+ selectionClassName?: string;
}
export function SelectionControl({
@@ -38,6 +39,7 @@ export function SelectionControl({
rowIndex,
itemKey,
verticalAlign = 'middle',
+ selectionClassName,
onChange,
...sharedProps
}: SelectionControlProps) {
@@ -123,7 +125,7 @@ export function SelectionControl({
onMouseUp={setShiftState}
onClick={handleClick}
htmlFor={controlId}
- className={clsx(styles.label, styles.root, verticalAlign === 'top' && styles['label-top'])}
+ className={clsx(styles.label, styles.root, verticalAlign === 'top' && styles['label-top'], selectionClassName)}
aria-label={ariaLabel}
title={ariaLabel}
{...(rowIndex !== undefined && !sharedProps.disabled
diff --git a/src/table/styles.scss b/src/table/styles.scss
index 186090c9bd..2a817981d8 100644
--- a/src/table/styles.scss
+++ b/src/table/styles.scss
@@ -8,6 +8,8 @@
@use '@cloudscape-design/component-toolkit/internal/focus-visible' as focus-visible;
@use '../container/shared' as container;
+/* stylelint-disable custom-property-pattern */
+
.root {
@include styles.default-text-style;
inline-size: 100%;
@@ -153,7 +155,7 @@ filter search icon.
border-start-end-radius: 0;
border-end-start-radius: 0;
border-end-end-radius: 0;
- background: awsui.$color-background-table-header;
+ background: var(--awsui-style-table-header-background, #{awsui.$color-background-table-header});
&.variant-full-page {
background: awsui.$color-background-layout-main;
}
diff --git a/src/table/thead.tsx b/src/table/thead.tsx
index 10024c014e..52a7e8290d 100644
--- a/src/table/thead.tsx
+++ b/src/table/thead.tsx
@@ -19,6 +19,8 @@ import styles from './styles.css.js';
export interface TheadProps {
selectionType: undefined | InternalSelectionType;
+ getSelectAllProps?: () => ItemSelectionProps;
+ selectionClassName?: string;
columnDefinitions: ReadonlyArray>;
sortingColumn: TableProps.SortingColumn | undefined;
sortingDescending: boolean | undefined;
@@ -27,7 +29,6 @@ export interface TheadProps {
tableVariant?: TableProps.Variant;
wrapLines: boolean | undefined;
resizableColumns: boolean | undefined;
- getSelectAllProps?: () => ItemSelectionProps;
onFocusMove: ((sourceElement: HTMLElement, fromIndex: number, direction: -1 | 1) => void) | undefined;
onResizeFinish: (newWidths: Map) => void;
onSortingChange: NonCancelableEventHandler> | undefined;
@@ -52,6 +53,7 @@ const Thead = React.forwardRef(
{
selectionType,
getSelectAllProps,
+ selectionClassName,
columnDefinitions,
sortingColumn,
sortingDisabled,
@@ -115,6 +117,7 @@ const Thead = React.forwardRef(
getSelectAllProps={getSelectAllProps}
onFocusMove={onFocusMove}
singleSelectionHeaderAriaLabel={singleSelectionHeaderAriaLabel}
+ selectionClassName={selectionClassName}
/>
) : null}
diff --git a/src/tabs/index.tsx b/src/tabs/index.tsx
index ad54fcde07..b9143398f2 100644
--- a/src/tabs/index.tsx
+++ b/src/tabs/index.tsx
@@ -56,6 +56,7 @@ export default function Tabs({
keyboardActivationMode = 'automatic',
actions,
style,
+ classNames,
...rest
}: TabsProps) {
for (const tab of tabs) {
@@ -165,7 +166,7 @@ export default function Tabs({
header={header}
disableHeaderPaddings={true}
{...baseProps}
- className={clsx(baseProps.className, styles.root)}
+ className={clsx(baseProps.className, classNames?.root, styles.root)}
__internalRootRef={__internalRootRef}
__contentKey={activeTabId}
disableContentPaddings={true}
@@ -182,7 +183,9 @@ export default function Tabs({
return (
diff --git a/src/tabs/interfaces.ts b/src/tabs/interfaces.ts
index 57090379b8..198fdb41e8 100644
--- a/src/tabs/interfaces.ts
+++ b/src/tabs/interfaces.ts
@@ -109,8 +109,20 @@ export interface TabsProps extends BaseComponentProps {
* @awsuiSystem core
*/
style?: TabsProps.Style;
+
+ /**
+ * An object that maps the tabs' slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The tabs' root element.
+ * @awsuiSystem core
+ */
+ classNames?: TabsProps.ClassNames;
}
export namespace TabsProps {
+ export interface ClassNames {
+ root?: string;
+ }
+
export type Variant = 'default' | 'container' | 'stacked';
export interface Tab {
diff --git a/src/tabs/tab-header-bar.scss b/src/tabs/tab-header-bar.scss
index 86f233dee9..7b7446b4f9 100644
--- a/src/tabs/tab-header-bar.scss
+++ b/src/tabs/tab-header-bar.scss
@@ -4,6 +4,7 @@
*/
/* stylelint-disable selector-max-type */
+/* stylelint-disable custom-property-pattern */
@use '../internal/styles' as styles;
@use '../internal/styles/tokens' as awsui;
@use '../internal/styles//foundation' as foundation;
@@ -135,24 +136,30 @@ $label-horizontal-spacing: awsui.$space-xs;
inset-inline-start: 0;
inline-size: calc(100% - 1px);
inset-block-end: calc(-1 * #{awsui.$border-divider-section-width});
- block-size: var(#{custom-props.$styleTabsActiveIndicatorWidth}, awsui.$border-active-width);
+ block-size: var(
+ --awsui-style-tab-active-indicator-width,
+ var(#{custom-props.$styleTabsActiveIndicatorWidth}, awsui.$border-active-width)
+ );
border-start-start-radius: var(
- #{custom-props.$styleTabsActiveIndicatorBorderRadius},
- awsui.$border-radius-tabs-focus-ring
+ --awsui-style-tab-active-indicator-border-radius,
+ var(#{custom-props.$styleTabsActiveIndicatorBorderRadius}, awsui.$border-radius-tabs-focus-ring)
);
border-start-end-radius: var(
- #{custom-props.$styleTabsActiveIndicatorBorderRadius},
- awsui.$border-radius-tabs-focus-ring
+ --awsui-style-tab-active-indicator-border-radius,
+ var(#{custom-props.$styleTabsActiveIndicatorBorderRadius}, awsui.$border-radius-tabs-focus-ring)
);
border-end-start-radius: var(
- #{custom-props.$styleTabsActiveIndicatorBorderRadius},
- awsui.$border-radius-tabs-focus-ring
+ --awsui-style-tab-active-indicator-border-radius,
+ var(#{custom-props.$styleTabsActiveIndicatorBorderRadius}, awsui.$border-radius-tabs-focus-ring)
);
border-end-end-radius: var(
- #{custom-props.$styleTabsActiveIndicatorBorderRadius},
- awsui.$border-radius-tabs-focus-ring
+ --awsui-style-tab-active-indicator-border-radius,
+ var(#{custom-props.$styleTabsActiveIndicatorBorderRadius}, awsui.$border-radius-tabs-focus-ring)
+ );
+ background: var(
+ --awsui-style-tab-active-indicator-color,
+ var(#{custom-props.$styleTabsActiveIndicatorColor}, #{awsui.$color-border-tabs-underline})
);
- background: var(#{custom-props.$styleTabsActiveIndicatorColor}, awsui.$color-border-tabs-underline);
opacity: 0;
}
@@ -172,8 +179,11 @@ $label-horizontal-spacing: awsui.$space-xs;
&:before {
content: '';
position: absolute;
- border-inline-end: var(#{custom-props.$styleTabsSeparatorWidth}, awsui.$border-divider-section-width) solid
- var(#{custom-props.$styleTabsSeparatorColor}, $separator-color);
+ border-inline-end: var(
+ --awsui-style-tab-separator-width,
+ var(#{custom-props.$styleTabsSeparatorWidth}, awsui.$border-divider-section-width)
+ )
+ solid var(--awsui-style-tab-separator-color, var(#{custom-props.$styleTabsSeparatorColor}, $separator-color));
inset: awsui.$space-scaled-s 0;
opacity: 1;
}
@@ -195,14 +205,22 @@ $label-horizontal-spacing: awsui.$space-xs;
padding-inline: 0;
margin-block-start: 1px;
- border-block: awsui.$border-divider-section-width solid var(#{custom-props.$styleBorderColorDefault}, transparent);
- border-inline: awsui.$border-divider-section-width solid var(#{custom-props.$styleBorderColorDefault}, transparent);
+ border-block: awsui.$border-divider-section-width solid
+ var(--awsui-style-tab-border-color-default, var(#{custom-props.$styleBorderColorDefault}, transparent));
+ border-inline: awsui.$border-divider-section-width solid
+ var(--awsui-style-tab-border-color-default, var(#{custom-props.$styleBorderColorDefault}, transparent));
font-size: awsui.$font-size-tabs;
line-height: awsui.$line-height-tabs;
font-weight: awsui.$font-weight-tabs;
- color: var(#{custom-props.$styleColorDefault}, awsui.$color-text-interactive-default);
- background-color: var(#{custom-props.$styleBackgroundDefault}, transparent);
+ color: var(
+ --awsui-style-tab-color-default,
+ var(#{custom-props.$styleColorDefault}, #{awsui.$color-text-interactive-default})
+ );
+ background-color: var(
+ --awsui-style-tab-background-default,
+ var(#{custom-props.$styleBackgroundDefault}, transparent)
+ );
padding-inline-start: calc(#{awsui.$space-xxs} - 1px);
padding-inline-end: awsui.$space-xxs;
@@ -216,14 +234,14 @@ $label-horizontal-spacing: awsui.$space-xs;
}
&:hover {
- color: var(#{custom-props.$styleColorHover}, awsui.$color-text-accent);
+ color: var(--awsui-style-tab-color-hover, var(#{custom-props.$styleColorHover}, #{awsui.$color-text-accent}));
border-color: var(
- #{custom-props.$styleBorderColorHover},
- var(#{custom-props.$styleBorderColorDefault}, transparent)
+ --awsui-style-tab-border-color-hover,
+ var(#{custom-props.$styleBorderColorHover}, var(#{custom-props.$styleBorderColorDefault}, transparent))
);
background-color: var(
- #{custom-props.$styleBackgroundHover},
- var(#{custom-props.$styleBackgroundDefault}, transparent)
+ --awsui-style-tab-background-hover,
+ var(#{custom-props.$styleBackgroundHover}, var(#{custom-props.$styleBackgroundDefault}, transparent))
);
}
@@ -269,17 +287,26 @@ $label-horizontal-spacing: awsui.$space-xs;
&,
&:hover {
cursor: default;
- color: var(#{custom-props.$styleColorDisabled}, awsui.$color-text-interactive-disabled);
- border-color: var(#{custom-props.$styleBorderColorDisabled}, transparent);
- background-color: var(#{custom-props.$styleBackgroundDisabled}, transparent);
+ color: var(
+ --awsui-style-tab-color-disabled,
+ var(#{custom-props.$styleColorDisabled}, #{awsui.$color-text-interactive-disabled})
+ );
+ border-color: var(
+ --awsui-style-tab-border-color-disabled,
+ var(#{custom-props.$styleBorderColorDisabled}, transparent)
+ );
+ background-color: var(
+ --awsui-style-tab-background-disabled,
+ var(#{custom-props.$styleBackgroundDisabled}, transparent)
+ );
font-weight: awsui.$font-weight-tabs-disabled;
}
}
.tabs-tab-active:not(.tabs-tab-disabled) {
- color: var(#{custom-props.$styleColorActive}, awsui.$color-text-accent);
- border-color: var(#{custom-props.$styleBorderColorActive}, transparent);
- background-color: var(#{custom-props.$styleBackgroundActive}, transparent);
+ color: var(--awsui-style-tab-color-active, var(#{custom-props.$styleColorActive}, #{awsui.$color-text-accent}));
+ border-color: var(--awsui-style-tab-border-color-active, var(#{custom-props.$styleBorderColorActive}, transparent));
+ background-color: var(--awsui-style-tab-background-active, var(#{custom-props.$styleBackgroundActive}, transparent));
&:after {
opacity: 1;
}
@@ -292,3 +319,4 @@ $label-horizontal-spacing: awsui.$space-xs;
.tabs-tab-focusable {
/* used to manage focusable logic */
}
+/* stylelint-enable custom-property-pattern */
diff --git a/src/text-filter/interfaces.ts b/src/text-filter/interfaces.ts
index 3ea4613dd0..efab51e310 100644
--- a/src/text-filter/interfaces.ts
+++ b/src/text-filter/interfaces.ts
@@ -66,6 +66,14 @@ export interface TextFilterProps extends BaseComponentProps, FormFieldControlPro
* @awsuiSystem core
*/
style?: TextFilterProps.Style;
+
+ /**
+ * An object that maps the text filter's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The text filter's root element.
+ * @awsuiSystem core
+ */
+ classNames?: TextFilterProps.ClassNames;
}
export namespace TextFilterProps {
@@ -73,6 +81,10 @@ export namespace TextFilterProps {
filteringText: string;
}
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface Ref {
/**
* Sets focus on the underlying input control.
diff --git a/src/text-filter/internal.tsx b/src/text-filter/internal.tsx
index 90f1abd513..9f5d6ced9b 100644
--- a/src/text-filter/internal.tsx
+++ b/src/text-filter/internal.tsx
@@ -38,6 +38,7 @@ const InternalTextFilter = React.forwardRef(
onDelayedChange,
loading = false,
style,
+ classNames,
__internalRootRef,
...rest
}: InternalTextFilterProps,
@@ -62,7 +63,7 @@ const InternalTextFilter = React.forwardRef(
});
return (
-
+
@@ -107,7 +108,7 @@ const Textarea = React.forwardRef(
return (
diff --git a/src/textarea/interfaces.ts b/src/textarea/interfaces.ts
index 66068c0f98..0f20eb2f01 100644
--- a/src/textarea/interfaces.ts
+++ b/src/textarea/interfaces.ts
@@ -57,11 +57,23 @@ export interface TextareaProps
* @awsuiSystem core
*/
style?: TextareaProps.Style;
+
+ /**
+ * An object that maps the textarea's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The textarea's root element.
+ * @awsuiSystem core
+ */
+ classNames?: TextareaProps.ClassNames;
}
export namespace TextareaProps {
export type KeyDetail = BaseKeyDetail;
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface ChangeDetail {
/**
* The new value of this textarea.
diff --git a/src/textarea/styles.scss b/src/textarea/styles.scss
index 3624bddc81..e4d8f74b60 100644
--- a/src/textarea/styles.scss
+++ b/src/textarea/styles.scss
@@ -8,6 +8,8 @@
@use '../internal/styles/foundation' as foundation;
@use '../internal/generated/custom-css-properties/index.scss' as custom-props;
+/* stylelint-disable custom-property-pattern */
+
.root {
/* used for test-utils */
}
@@ -21,80 +23,132 @@
// Allow multi-line placeholders
white-space: pre-wrap;
- padding-block: styles.$control-padding-vertical;
- padding-inline: styles.$control-padding-horizontal;
+ padding-block: var(--awsui-style-input-padding-block, #{styles.$control-padding-vertical});
+ padding-inline: var(--awsui-style-input-padding-inline, #{styles.$control-padding-horizontal});
- color: var(#{custom-props.$styleColorDefault}, awsui.$color-text-body-default);
+ color: var(--awsui-style-input-color, var(#{custom-props.$styleColorDefault}, awsui.$color-text-body-default));
max-inline-size: 100%;
inline-size: 100%;
display: block;
box-sizing: border-box;
- background-color: var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default);
- border-start-start-radius: styles.$control-border-radius;
- border-start-end-radius: styles.$control-border-radius;
- border-end-start-radius: styles.$control-border-radius;
- border-end-end-radius: styles.$control-border-radius;
-
- border-block: awsui.$border-width-field solid
- var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default);
- border-inline: awsui.$border-width-field solid
- var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default);
+ background-color: var(
+ --awsui-style-input-background,
+ var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default)
+ );
+ border-start-start-radius: var(--awsui-style-input-border-radius, #{styles.$control-border-radius});
+ border-start-end-radius: var(--awsui-style-input-border-radius, #{styles.$control-border-radius});
+ border-end-start-radius: var(--awsui-style-input-border-radius, #{styles.$control-border-radius});
+ border-end-end-radius: var(--awsui-style-input-border-radius, #{styles.$control-border-radius});
+
+ border-block: var(--awsui-style-input-border-width, #{awsui.$border-width-field}) solid
+ var(
+ --awsui-style-input-border-color,
+ var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default)
+ );
+ border-inline: var(--awsui-style-input-border-width, #{awsui.$border-width-field}) solid
+ var(
+ --awsui-style-input-border-color,
+ var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default)
+ );
- box-shadow: var(#{custom-props.$styleBoxShadowDefault});
+ box-shadow: var(--awsui-style-input-box-shadow-default, var(#{custom-props.$styleBoxShadowDefault}));
@include styles.font-body-m;
+ font-size: var(--awsui-style-input-font-size, #{awsui.$font-size-body-m});
+ font-weight: var(--awsui-style-input-font-weight, inherit);
&:hover {
border-color: var(
- #{custom-props.$styleBorderColorHover},
- var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default)
+ --awsui-style-input-border-color-hover,
+ var(
+ --awsui-style-input-border-color,
+ var(
+ #{custom-props.$styleBorderColorHover},
+ var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-default)
+ )
+ )
);
color: var(
- #{custom-props.$styleColorHover},
- var(#{custom-props.$styleBorderColorDefault}, awsui.$color-text-body-default)
+ --awsui-style-input-color-hover,
+ var(
+ #{custom-props.$styleColorHover},
+ var(#{custom-props.$styleBorderColorDefault}, awsui.$color-text-body-default)
+ )
);
background-color: var(
- #{custom-props.$styleBackgroundHover},
- var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default)
+ --awsui-style-input-background-hover,
+ var(
+ --awsui-style-input-background,
+ var(
+ #{custom-props.$styleBackgroundHover},
+ var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default)
+ )
+ )
+ );
+ box-shadow: var(
+ --awsui-style-input-box-shadow-hover,
+ var(#{custom-props.$styleBoxShadowHover}, #{custom-props.$styleBoxShadowDefault})
);
- box-shadow: var(#{custom-props.$styleBoxShadowHover}, #{custom-props.$styleBoxShadowDefault});
}
&.textarea-readonly {
@include styles.form-readonly-element(
$background-color: var(
- #{custom-props.$styleBackgroundReadonly},
- var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default)
+ --awsui-style-input-background-readonly,
+ var(
+ #{custom-props.$styleBackgroundReadonly},
+ var(#{custom-props.$styleBackgroundDefault}, awsui.$color-background-input-default)
+ )
),
$border-color: var(
- #{custom-props.$styleBorderColorReadonly},
- var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-disabled)
+ --awsui-style-input-border-color-readonly,
+ var(
+ #{custom-props.$styleBorderColorReadonly},
+ var(#{custom-props.$styleBorderColorDefault}, awsui.$color-border-input-disabled)
+ )
)
);
color: var(
- #{custom-props.$styleColorReadonly},
- var(#{custom-props.$styleColorDefault}, awsui.$color-text-body-default)
+ --awsui-style-input-color-readonly,
+ var(#{custom-props.$styleColorReadonly}, var(#{custom-props.$styleColorDefault}, awsui.$color-text-body-default))
);
- box-shadow: var(#{custom-props.$styleBoxShadowReadonly});
+ box-shadow: var(--awsui-style-input-box-shadow-readonly, var(#{custom-props.$styleBoxShadowReadonly}));
}
&::placeholder {
@include styles.form-placeholder(
- $color: var(#{custom-props.$stylePlaceholderColor}, awsui.$color-text-input-placeholder),
- $font-size: var(#{custom-props.$stylePlaceholderFontSize}),
- $font-style: var(#{custom-props.$stylePlaceholderFontStyle}, italic),
- $font-weight: var(#{custom-props.$stylePlaceholderFontWeight})
+ $color: var(
+ --awsui-style-input-placeholder-color,
+ var(#{custom-props.$stylePlaceholderColor}, awsui.$color-text-input-placeholder)
+ ),
+ $font-size: var(--awsui-style-input-placeholder-font-size, var(#{custom-props.$stylePlaceholderFontSize})),
+ $font-style: var(
+ --awsui-style-input-placeholder-font-style,
+ var(#{custom-props.$stylePlaceholderFontStyle}, italic)
+ ),
+ $font-weight: var(--awsui-style-input-placeholder-font-weight, var(#{custom-props.$stylePlaceholderFontWeight}))
);
opacity: 1;
}
&:focus {
@include styles.form-focus-element(
- $border-color: var(#{custom-props.$styleBorderColorFocus}, awsui.$color-border-input-focused),
- $box-shadow: var(#{custom-props.$styleBoxShadowFocus}, foundation.$box-shadow-focused-light)
+ $border-radius: var(--awsui-style-input-border-radius, #{styles.$control-border-radius}),
+ $border-width: var(--awsui-style-input-border-width, #{awsui.$border-width-field}),
+ $border-color: var(
+ --awsui-style-input-border-color-focus,
+ var(#{custom-props.$styleBorderColorFocus}, awsui.$color-border-input-focused)
+ ),
+ $box-shadow: var(
+ --awsui-style-input-focus-ring-shadow,
+ var(#{custom-props.$styleBoxShadowFocus}, foundation.$box-shadow-focused-light)
+ )
+ );
+ color: var(--awsui-style-input-color-focus, var(#{custom-props.$styleColorFocus}, awsui.$color-text-body-default));
+ background-color: var(
+ --awsui-style-input-background-focus,
+ var(#{custom-props.$styleBackgroundFocus}, awsui.$color-background-input-default)
);
- color: var(#{custom-props.$styleColorFocus}, awsui.$color-text-body-default);
- background-color: var(#{custom-props.$styleBackgroundFocus}, awsui.$color-background-input-default);
}
&:invalid {
@@ -104,12 +158,21 @@
&:disabled {
@include styles.form-disabled-element(
- $background-color: var(#{custom-props.$styleBackgroundDisabled}, awsui.$color-background-input-disabled),
- $border-color: var(#{custom-props.$styleBorderColorDisabled}, awsui.$color-border-input-disabled),
- $color: var(#{custom-props.$styleColorDisabled}, awsui.$color-text-input-disabled),
+ $background-color: var(
+ --awsui-style-input-background-disabled,
+ var(#{custom-props.$styleBackgroundDisabled}, awsui.$color-background-input-disabled)
+ ),
+ $border-color: var(
+ --awsui-style-input-border-color-disabled,
+ var(#{custom-props.$styleBorderColorDisabled}, awsui.$color-border-input-disabled)
+ ),
+ $color: var(
+ --awsui-style-input-color-disabled,
+ var(#{custom-props.$styleColorDisabled}, awsui.$color-text-input-disabled)
+ ),
$cursor: default
);
- box-shadow: var(#{custom-props.$styleBoxShadowDisabled});
+ box-shadow: var(--awsui-style-input-box-shadow-disabled, var(#{custom-props.$styleBoxShadowDisabled}));
&::placeholder {
@include styles.form-placeholder-disabled;
opacity: 1;
diff --git a/src/toggle/interfaces.ts b/src/toggle/interfaces.ts
index c5e4848eab..0681af1068 100644
--- a/src/toggle/interfaces.ts
+++ b/src/toggle/interfaces.ts
@@ -29,6 +29,14 @@ export interface ToggleProps extends BaseCheckboxProps {
*/
style?: ToggleProps.Style;
+ /**
+ * An object that maps the toggle's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The toggle's root element.
+ * @awsuiSystem core
+ */
+ classNames?: ToggleProps.ClassNames;
+
/**
* Attributes to add to the native `input` element.
* Some attributes will be automatically combined with internal attribute values:
@@ -50,6 +58,10 @@ export namespace ToggleProps {
focus(): void;
}
+ export interface ClassNames {
+ root?: string;
+ }
+
export interface ChangeDetail {
checked: boolean;
}
diff --git a/src/toggle/internal.tsx b/src/toggle/internal.tsx
index 480be02bdf..171aef7ada 100644
--- a/src/toggle/internal.tsx
+++ b/src/toggle/internal.tsx
@@ -43,6 +43,7 @@ const InternalToggle = React.forwardRef(
nativeInputAttributes,
__internalRootRef,
style,
+ classNames,
__injectAnalyticsComponentMetadata,
...rest
},
@@ -73,7 +74,7 @@ const InternalToggle = React.forwardRef(
return (
.dismiss-button {
- color: var(#{custom-props.$tokenStyleDismissColorReadOnly}, awsui.$color-text-button-inline-icon-disabled);
+ color: var(
+ --awsui-style-token-dismiss-color-readonly,
+ var(#{custom-props.$tokenStyleDismissColorReadOnly}, awsui.$color-text-button-inline-icon-disabled)
+ );
}
}
.token-box-disabled {
pointer-events: none;
- border-color: var(#{custom-props.$tokenStyleBorderColorDisabled}, awsui.$color-border-control-disabled);
- background: var(#{custom-props.$tokenStyleBackgroundDisabled}, awsui.$color-background-container-content);
+ border-color: var(
+ --awsui-style-token-border-color-disabled,
+ var(#{custom-props.$tokenStyleBorderColorDisabled}, awsui.$color-border-control-disabled)
+ );
+ background: var(
+ --awsui-style-token-background-disabled,
+ var(#{custom-props.$tokenStyleBackgroundDisabled}, awsui.$color-background-container-content)
+ );
color: awsui.$color-text-disabled;
// stylelint-disable-next-line no-descending-specificity
> .dismiss-button {
- color: var(#{custom-props.$tokenStyleDismissColorDisabled}, awsui.$color-text-button-inline-icon-disabled);
+ color: var(
+ --awsui-style-token-dismiss-color-disabled,
+ var(#{custom-props.$tokenStyleDismissColorDisabled}, awsui.$color-text-button-inline-icon-disabled)
+ );
}
}
diff --git a/src/top-navigation/interfaces.ts b/src/top-navigation/interfaces.ts
index 3bad629d32..9aaa6b2bf9 100644
--- a/src/top-navigation/interfaces.ts
+++ b/src/top-navigation/interfaces.ts
@@ -66,9 +66,23 @@ export interface TopNavigationProps extends BaseComponentProps {
* @i18n
*/
i18nStrings?: TopNavigationProps.I18nStrings;
+
+ /**
+ * An object that maps the top navigation's slots to CSS class names for custom styling.
+ * Use these classes to scope `--awsui-style-*` custom properties.
+ * * `root` - The top navigation's root element.
+ * * `utility` - Applied to each utility wrapper. Accepts a string or a function receiving `{ utility }` and returning a string.
+ * @awsuiSystem core
+ */
+ classNames?: TopNavigationProps.ClassNames;
}
export namespace TopNavigationProps {
+ export interface ClassNames {
+ root?: string;
+ utility?: string | ((args: { utility: TopNavigationProps.Utility }) => string);
+ }
+
export interface Identity {
title?: string;
logo?: Logo;
@@ -93,6 +107,10 @@ export namespace TopNavigationProps {
badge?: boolean;
disableUtilityCollapse?: boolean;
disableTextCollapse?: boolean;
+ /**
+ * @deprecated Use `classNames.utility` instead.
+ */
+ className?: string;
}
export interface MenuDropdownUtility extends BaseUtility {
diff --git a/src/top-navigation/internal.tsx b/src/top-navigation/internal.tsx
index eea5af6761..29b1d4a484 100644
--- a/src/top-navigation/internal.tsx
+++ b/src/top-navigation/internal.tsx
@@ -27,6 +27,7 @@ export default function InternalTopNavigation({
i18nStrings,
utilities,
search,
+ classNames,
...restProps
}: InternalTopNavigationProps) {
checkSafeUrl('TopNavigation', identity.href);
@@ -47,6 +48,14 @@ export default function InternalTopNavigation({
}
};
+ const resolvedUtilities = utilities.map(utility => ({
+ ...utility,
+ className:
+ typeof classNames?.utility === 'function'
+ ? classNames.utility({ utility })
+ : (classNames?.utility ?? utility.className),
+ }));
+
const toggleOverflowMenu = () => {
setOverflowMenuOpen(overflowMenuOpen => !overflowMenuOpen);
};
@@ -142,14 +151,14 @@ export default function InternalTopNavigation({
)}
{showUtilities &&
- utilities
+ resolvedUtilities
.filter(
(_utility, i) =>
isVirtual || !responsiveState.hideUtilities || responsiveState.hideUtilities.indexOf(i) === -1
)
.map((utility, i) => {
const hideText = !!responsiveState.hideUtilityText;
- const isLast = (isVirtual || !showMenuTrigger) && i === utilities.length - 1;
+ const isLast = (isVirtual || !showMenuTrigger) && i === resolvedUtilities.length - 1;
const offsetRight = isLast && isLargeViewport ? 'xxl' : isLast ? 'l' : undefined;
return (
@@ -173,9 +182,9 @@ export default function InternalTopNavigation({
})}
{isVirtual &&
- utilities.map((utility, i) => {
+ resolvedUtilities.map((utility, i) => {
const hideText = !responsiveState.hideUtilityText;
- const isLast = !showMenuTrigger && i === utilities.length - 1;
+ const isLast = !showMenuTrigger && i === resolvedUtilities.length - 1;
const offsetRight = isLast && isLargeViewport ? 'xxl' : isLast ? 'l' : undefined;
return (
@@ -223,7 +232,7 @@ export default function InternalTopNavigation({
};
return (
-
+
{/* Render virtual content first to ensure React refs for content will be assigned on the actual nodes. */}
{content(true)}
@@ -236,7 +245,7 @@ export default function InternalTopNavigation({
headerText={i18nStrings?.overflowMenuTitleText}
dismissIconAriaLabel={i18nStrings?.overflowMenuDismissIconAriaLabel}
backIconAriaLabel={i18nStrings?.overflowMenuBackIconAriaLabel}
- items={utilities.filter(
+ items={resolvedUtilities.filter(
(utility, i) =>
(!responsiveState.hideUtilities || responsiveState.hideUtilities.indexOf(i) !== -1) &&
!utility.disableUtilityCollapse
diff --git a/src/top-navigation/parts/utility.tsx b/src/top-navigation/parts/utility.tsx
index 6fc0df1003..d63420e40d 100644
--- a/src/top-navigation/parts/utility.tsx
+++ b/src/top-navigation/parts/utility.tsx
@@ -34,7 +34,7 @@ export default function Utility({ hideText, definition, offsetRight }: UtilityPr
checkSafeUrl('TopNavigation', definition.href);
if (definition.variant === 'primary-button') {
return (
-
+
+
- {!shouldHideText && definition.text}
-
+
+
+ {!shouldHideText && definition.text}
+
+
);
}
diff --git a/src/top-navigation/styles.scss b/src/top-navigation/styles.scss
index 6167698a8a..1711da593b 100644
--- a/src/top-navigation/styles.scss
+++ b/src/top-navigation/styles.scss
@@ -7,9 +7,11 @@
@use '../internal/styles/tokens' as awsui;
@use '@cloudscape-design/component-toolkit/internal/focus-visible' as focus-visible;
+/* stylelint-disable custom-property-pattern */
+
.top-navigation {
@include styles.styles-reset;
- background: awsui.$color-background-container-content;
+ background: var(--awsui-style-top-navigation-background, #{awsui.$color-background-container-content});
border-block-end: awsui.$border-divider-section-width solid awsui.$color-border-divider-default;
> .padding-box {
@@ -62,10 +64,10 @@
display: flex;
align-items: center;
text-decoration: none;
- color: awsui.$color-text-top-navigation-title;
+ color: var(--awsui-style-top-navigation-title-color, #{awsui.$color-text-top-navigation-title});
&:hover {
- color: awsui.$color-text-accent;
+ color: var(--awsui-style-top-navigation-title-color-hover, #{awsui.$color-text-accent});
}
@include focus-visible.when-visible {
@@ -217,7 +219,7 @@
.overflow-menu {
@include styles.styles-reset;
- background: awsui.$color-background-container-content;
+ background: var(--awsui-style-top-navigation-background, #{awsui.$color-background-container-content});
block-size: 100%;
}