diff --git a/apps/website/pages/foundations/elevation.tsx b/apps/website/pages/foundations/elevation.tsx
new file mode 100644
index 000000000..78151e4e3
--- /dev/null
+++ b/apps/website/pages/foundations/elevation.tsx
@@ -0,0 +1,13 @@
+import Head from "next/head";
+import ElevationPage from "screens/foundations/elevation/ElevationPage";
+
+const Elevation = () => (
+ <>
+
+ Elevation — Halstack Design System
+
+
+ >
+);
+
+export default Elevation;
diff --git a/apps/website/screens/common/pagesList.ts b/apps/website/screens/common/pagesList.ts
index 4417ce237..c6d771d1b 100644
--- a/apps/website/screens/common/pagesList.ts
+++ b/apps/website/screens/common/pagesList.ts
@@ -40,6 +40,7 @@ const principlesLinks: LinkDetails[] = [
const foundationsLinks: LinkDetails[] = [
{ label: "Color", path: "/foundations/color" },
+ { label: "Elevation", path: "/foundations/elevation" },
{ label: "Height", path: "/foundations/height" },
{ label: "Iconography", path: "/foundations/iconography" },
{ label: "Spacing", path: "/foundations/spacing" },
diff --git a/apps/website/screens/components/container/code/examples/listbox.tsx b/apps/website/screens/components/container/code/examples/listbox.tsx
index df8a4d9ff..1b490b42b 100644
--- a/apps/website/screens/components/container/code/examples/listbox.tsx
+++ b/apps/website/screens/components/container/code/examples/listbox.tsx
@@ -13,7 +13,7 @@ const code = `() => {
width: "var(--border-width-s)"
}}
borderRadius="var(--border-radius-s)"
- boxShadow="var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) var(--shadow-light)"
+ boxShadow="var(--shadow-200)"
boxSizing="border-box"
maxHeight="304px"
overflow={{ x: "hidden", y: "auto" }}
diff --git a/apps/website/screens/foundations/elevation/ElevationPage.tsx b/apps/website/screens/foundations/elevation/ElevationPage.tsx
new file mode 100644
index 000000000..0cf6c76d8
--- /dev/null
+++ b/apps/website/screens/foundations/elevation/ElevationPage.tsx
@@ -0,0 +1,180 @@
+import { DxcHeading, DxcFlex, DxcTable, DxcParagraph, DxcBulletedList } from "@dxc-technology/halstack-react";
+import Image from "@/common/Image";
+import Code from "@/common/Code";
+import QuickNavContainer from "@/common/QuickNavContainer";
+import PageHeading from "@/common/PageHeading";
+import DocFooter from "@/common/DocFooter";
+import QuickNavContainerLayout from "@/common/QuickNavContainerLayout";
+import shadows from "./images/shadows.jpg";
+import Figure from "@/common/Figure";
+
+const sections = [
+ {
+ title: "Introduction",
+ content: (
+ <>
+
+ Shadows are a fundamental part of visual design systems. In Halstack, we use them, along with colors, to{" "}
+ create depth and layering on the interface. They help users distinguish between surfaces,
+ understand component hierarchy, and focus their attention on key elements.
+
+
+ By simulating how light interacts with objects, shadows reinforce a clear spatial structure in digital
+ interfaces, much like in the physical world. Whether it's emphasizing a modal over a background or giving
+ subtle prominence to a card, elevation contributes both aesthetically and functionally to the user experience.
+
+ >
+ ),
+ },
+ {
+ title: "Shadow tokens",
+ content: (
+ <>
+
+ Halstack provides a set of predefined shadow tokens optimized for clarity and performance across our products.
+ Each token corresponds to a specific elevation level with calibrated values for offset, blur, and color
+ transparency.
+
+
+
+
+ | Token |
+ X position |
+ Y position |
+ Blur |
+ Spread |
+ color |
+
+
+
+
+
+ shadow-100
+ |
+ 0px |
+ 2px |
+ 2px |
+ 0px |
+
+ color-grey-400-a
+ |
+
+
+
+ shadow-200
+ |
+ 0px |
+ 12px |
+ 12px |
+ 0px |
+
+ color-grey-300-a
+ |
+
+
+
+ shadow-300
+ |
+ 0px |
+ 24px |
+ 24px |
+ 0px |
+
+ color-grey-300-a
+ |
+
+
+
+ shadow-400
+ |
+ 0px |
+ 48px |
+ 48px |
+ 0px |
+
+ color-grey-300-a
+ |
+
+
+
+
+
+
+ >
+ ),
+ },
+ {
+ title: "Shadow guidelines by scale",
+ content: (
+ <>
+
+ Each shadow style is designed to serve a different level of emphasis or structural role in the UI. Below are
+ some typical use cases per shadow level:
+
+
+
+ shadow-100: creates subtle separation from the background without drawing too much
+ attention, such as small UI elements like buttons, input fields, or lightweight cards.
+
+
+ shadow-200: signals a slight lift and draws more attention than shadow-100, especially
+ useful for elements that temporarily appear above the rest of the UI; such as cards, dashboard, popovers, or
+ dropdowns.
+
+
+ shadow-300: used for modals, bottom sheets, or floating panels; as it clearly separates
+ important, interactive components from the rest of the content.
+
+
+ shadow-400: provides the strongest visual depth to ensure clear hierarchy and focus in the
+ UI. A few examples where this shadow can be applied are full-screen overlays, onboarding dialogs, or focused
+ system alerts.
+
+
+ >
+ ),
+ },
+ {
+ title: "Best practices",
+ content: (
+
+
+ Use elevation purposefully: shadows are not decorative. Apply them to communicate visual
+ hierarchy and component behavior.
+
+
+ Don’t overlay too much: avoid stacking multiple shadows or using high-intensity shadows on
+ too many components, as this leads to visual clutter.
+
+
+ Stay within the core scale: Use only the defined shadow tokens unless there's a validated
+ need for a custom elevation.
+
+
+ Avoid using shadows as borders: If you need to separate elements or define boundaries, use
+ spacing or a border token instead.
+
+
+ Consistency across themes: Our shadow tokens are designed to adapt well across themes and
+ backgrounds. Avoid tweaking individual values unless strictly necessary.
+
+
+ ),
+ },
+];
+
+export default function ElevationPage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/screens/foundations/elevation/images/shadows.jpg b/apps/website/screens/foundations/elevation/images/shadows.jpg
new file mode 100644
index 000000000..b32bb34a8
Binary files /dev/null and b/apps/website/screens/foundations/elevation/images/shadows.jpg differ
diff --git a/apps/website/screens/foundations/spacing/SpacingPage.tsx b/apps/website/screens/foundations/spacing/SpacingPage.tsx
index 4c81e0507..9b30a3089 100644
--- a/apps/website/screens/foundations/spacing/SpacingPage.tsx
+++ b/apps/website/screens/foundations/spacing/SpacingPage.tsx
@@ -309,7 +309,7 @@ export default function SpacingPage() {
-
+
);
}
diff --git a/packages/lib/src/accordion/AccordionItem.tsx b/packages/lib/src/accordion/AccordionItem.tsx
index 2be01b20a..ae4925998 100644
--- a/packages/lib/src/accordion/AccordionItem.tsx
+++ b/packages/lib/src/accordion/AccordionItem.tsx
@@ -12,8 +12,7 @@ const AccordionContainer = styled.div`
flex-direction: column;
background-color: var(--color-bg-neutral-lightest);
border-radius: var(--border-radius-s);
- box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread)
- var(--shadow-light);
+ box-shadow: var(--shadow-200);
min-width: 280px;
width: 100%;
`;
diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx
index df063d222..6de93bc71 100644
--- a/packages/lib/src/card/Card.tsx
+++ b/packages/lib/src/card/Card.tsx
@@ -26,11 +26,7 @@ const Card = styled.div<{
}`}
border-radius: var(--border-radius-s);
box-shadow: ${({ shadowDepth }) =>
- shadowDepth === 1
- ? "var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread) var(--shadow-dark)"
- : shadowDepth === 2
- ? "var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) var(--shadow-light)"
- : "none"};
+ shadowDepth === 1 ? "var(--shadow-100)" : shadowDepth === 2 ? "var(--shadow-200)" : "none"};
`;
const CardContainer = styled.div<{
diff --git a/packages/lib/src/container/Container.stories.tsx b/packages/lib/src/container/Container.stories.tsx
index 7e4ade187..8846f18be 100644
--- a/packages/lib/src/container/Container.stories.tsx
+++ b/packages/lib/src/container/Container.stories.tsx
@@ -18,7 +18,7 @@ const Listbox = ({ suggestions = [] }: { suggestions: string[] }): JSX.Element =
style: "var(--border-style-default)",
}}
borderRadius="var(--border-radius-s)"
- boxShadow="var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) var(--shadow-light)"
+ boxShadow="var(--shadow-200)"
boxSizing="border-box"
maxHeight="304px"
overflow={{ x: "hidden", y: "auto" }}
diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx
index 30a308090..fb5b8f45c 100644
--- a/packages/lib/src/date-input/DatePicker.tsx
+++ b/packages/lib/src/date-input/DatePicker.tsx
@@ -11,8 +11,7 @@ import { HalstackLanguageContext } from "../HalstackContext";
const DatePickerContainer = styled.div`
padding: var(--spacing-padding-m) var(--spacing-padding-xs) var(--spacing-padding-xs) var(--spacing-padding-xs);
background-color: var(--color-bg-neutral-lightest);
- box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread)
- var(--shadow-light);
+ box-shadow: var(--shadow-200);
border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-medium);
border-radius: var(--border-radius-s);
width: fit-content;
diff --git a/packages/lib/src/date-input/YearPicker.tsx b/packages/lib/src/date-input/YearPicker.tsx
index 0579dc3fb..b08f0cd54 100644
--- a/packages/lib/src/date-input/YearPicker.tsx
+++ b/packages/lib/src/date-input/YearPicker.tsx
@@ -12,8 +12,7 @@ const YearPickerContainer = styled.div`
overflow-y: scroll;
width: 292px;
height: 312px;
- box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread)
- var(--shadow-light);
+ box-shadow: var(--shadow-200);
`;
const YearPickerButton = styled.button<{
diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx
index 6e9e16b4a..74a219234 100644
--- a/packages/lib/src/dialog/Dialog.tsx
+++ b/packages/lib/src/dialog/Dialog.tsx
@@ -43,7 +43,7 @@ const Dialog = styled.div<{ closable: DialogPropsType["closable"] }>`
border-radius: 4px;
background-color: var(--color-bg-neutral-lightest);
${(props) => props.closable && "min-height: 72px;"}
- box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread) var(--shadow-dark);
+ box-shadow: var(--shadow-100);
@media (max-width: ${responsiveSizes.medium}rem) {
max-width: 92%;
diff --git a/packages/lib/src/dropdown/DropdownMenu.tsx b/packages/lib/src/dropdown/DropdownMenu.tsx
index 3523af212..3473196bc 100644
--- a/packages/lib/src/dropdown/DropdownMenu.tsx
+++ b/packages/lib/src/dropdown/DropdownMenu.tsx
@@ -11,8 +11,7 @@ const DropdownMenuContainer = styled.ul`
margin: 0;
background-color: var(--color-bg-neutral-lightest);
border-radius: var(--border-radius-s);
- box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread)
- var(--shadow-dark);
+ box-shadow: var(--shadow-100);
outline: none;
overflow-y: auto;
z-index: var(--z-dropdown);
diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx
index 1294a9a74..1244232e7 100644
--- a/packages/lib/src/select/Listbox.tsx
+++ b/packages/lib/src/select/Listbox.tsx
@@ -15,8 +15,7 @@ const ListboxContainer = styled.div`
background-color: var(--color-bg-neutral-lightest);
border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-medium);
border-radius: var(--border-radius-s);
- box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread)
- var(--shadow-light);
+ box-shadow: var(--shadow-200);
color: var(--color-fg-neutral-dark);
font-family: var(--typography-font-family);
font-size: var(--typography-label-m);
diff --git a/packages/lib/src/styles/variables.css b/packages/lib/src/styles/variables.css
index 6107773cc..2289d9b56 100644
--- a/packages/lib/src/styles/variables.css
+++ b/packages/lib/src/styles/variables.css
@@ -268,8 +268,6 @@
--color-fg-warning-medium: var(--color-orange-500);
--color-fg-warning-strong: var(--color-orange-600);
--color-fg-warning-stronger: var(--color-orange-800);
- --shadow-dark: var(--color-alpha-400-a);
- --shadow-light: var(--color-alpha-300-a);
--border-radius-none: var(--dimensions-0);
--border-radius-xs: var(--dimensions-2);
--border-radius-s: var(--dimensions-4);
@@ -289,22 +287,10 @@
--height-xl: var(--dimensions-40);
--height-xxl: var(--dimensions-48);
--height-xxxl: var(--dimensions-56);
- --shadow-high-spread: var(--dimensions-0);
- --shadow-high-x-position: var(--dimensions-0);
- --shadow-high-blur: var(--dimensions-24);
- --shadow-high-y-position: var(--dimensions-24);
- --shadow-higher-spread: var(--dimensions-0);
- --shadow-higher-x-position: var(--dimensions-0);
- --shadow-higher-blur: var(--dimensions-48);
- --shadow-higher-y-position: var(--dimensions-48);
- --shadow-low-spread: var(--dimensions-0);
- --shadow-low-x-position: var(--dimensions-0);
- --shadow-low-blur: var(--dimensions-2);
- --shadow-low-y-position: var(--dimensions-2);
- --shadow-mid-spread: var(--dimensions-0);
- --shadow-mid-x-position: var(--dimensions-0);
- --shadow-mid-blur: var(--dimensions-12);
- --shadow-mid-y-position: var(--dimensions-12);
+ --shadow-100: var(--dimensions-0) var(--dimensions-2) var(--dimensions-2) var(--dimensions-0) var(--color-alpha-400-a);
+ --shadow-200: var(--dimensions-0) var(--dimensions-12) var(--dimensions-12) var(--dimensions-0) var(--color-alpha-300-a);
+ --shadow-300: var(--dimensions-0) var(--dimensions-24) var(--dimensions-24) var(--dimensions-0) var(--color-alpha-300-a);
+ --shadow-400: var(--dimensions-0) var(--dimensions-48) var(--dimensions-48) var(--dimensions-0) var(--color-alpha-300-a);
--spacing-gap-none: var(--dimensions-0);
--spacing-gap-xxs: var(--dimensions-2);
--spacing-gap-xs: var(--dimensions-4);
diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx
index 2cf68b113..5d3629c01 100644
--- a/packages/lib/src/switch/Switch.tsx
+++ b/packages/lib/src/switch/Switch.tsx
@@ -104,8 +104,7 @@ const Switch = styled.span<{ checked: SwitchPropsType["checked"]; disabled: Swit
height: var(--height-s);
background-color: var(--color-fg-neutral-bright);
border-radius: 50%;
- box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur)
- var(--shadow-low-spread) var(--shadow-dark);
+ box-shadow: var(--shadow-100);
transform: ${({ checked }) => checked && "translateX(20px)"};
transition: transform 0.2s ease-in-out; /* Thumb transform transition */
}
diff --git a/packages/lib/src/text-input/Suggestions.tsx b/packages/lib/src/text-input/Suggestions.tsx
index 9e9cfca5a..cd6563c32 100644
--- a/packages/lib/src/text-input/Suggestions.tsx
+++ b/packages/lib/src/text-input/Suggestions.tsx
@@ -13,8 +13,7 @@ const SuggestionsContainer = styled.div`
background-color: var(--color-bg-neutral-lightest);
border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-medium);
border-radius: var(--border-radius-s);
- box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread)
- var(--shadow-light);
+ box-shadow: var(--shadow-200);
color: var(--color-fg-neutral-dark);
font-family: var(--typography-font-family);
font-size: var(--typography-label-m);
diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx
index 8c7bfe98d..760fb5abc 100644
--- a/packages/lib/src/toast/Toast.tsx
+++ b/packages/lib/src/toast/Toast.tsx
@@ -40,8 +40,7 @@ const Toast = styled.output<{ semantic: ToastPropsType["semantic"]; isClosing: b
width: fit-content;
border-left: var(--border-width-m) var(--border-style-default) ${({ semantic }) => getSemantic(semantic).primaryColor};
border-radius: var(--border-radius-s);
- box-shadow: var(--shadow-low-x-position) var(--shadow-low-y-position) var(--shadow-low-blur) var(--shadow-low-spread)
- var(--shadow-dark);
+ box-shadow: var(--shadow-100);
display: inline-flex;
gap: var(--spacing-gap-l);
justify-content: space-between;