|
| 1 | +--- |
| 2 | +title: Icon |
| 3 | +--- |
| 4 | + |
| 5 | +# Icon |
| 6 | + |
| 7 | +Icons are small visual symbols that communicate meaning at a glance. They support text labels, guide users through interactions, and add clarity to our interfaces. The Icon component is designed with care to scale automatically with typography, adapt to density modes, and meet accessibility standards out of the box. |
| 8 | + |
| 9 | +<iframe |
| 10 | + class="sb-iframe" |
| 11 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--introduction" |
| 12 | + width="100%" |
| 13 | + height="88" |
| 14 | + frameborder="1" |
| 15 | +></iframe> |
| 16 | +
|
| 17 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--introduction) |
| 18 | + |
| 19 | +## When to Use |
| 20 | + |
| 21 | +Use Icon to reinforce meaning alongside text - in buttons, form fields, status messages, and navigation. Icons help users scan interfaces faster and understand actions without reading every label. |
| 22 | + |
| 23 | +**Avoid Icon for:** |
| 24 | + |
| 25 | +- Purely decorative illustrations - use an image instead |
| 26 | +- Complex graphics or logos - use a dedicated image or SVG |
| 27 | + |
| 28 | +## Structure |
| 29 | + |
| 30 | +The Icon component renders an SVG element with a single `data` prop from `@equinor/eds-icons`. It supports three sizing layers: |
| 31 | + |
| 32 | +- **Explicit `size` prop** - highest priority, for precise control |
| 33 | +- **Parent Typography** - automatically inherits the right size when placed inside Typography |
| 34 | +- **Dynamic fallback** - scales at 1.5× relative to the surrounding font size |
| 35 | + |
| 36 | +## Guidelines |
| 37 | + |
| 38 | +### Sizes |
| 39 | + |
| 40 | +Use the `size` prop for explicit sizing. Each size maps to an EDS design token, ensuring consistency across our interfaces. |
| 41 | + |
| 42 | +| Size | Spacious (default) | Comfortable (compact) | |
| 43 | +| ---- | ------------------ | --------------------- | |
| 44 | +| xs | 16px | 14px | |
| 45 | +| sm | 18px | 16px | |
| 46 | +| md | 20px | 18px | |
| 47 | +| lg | 24px | 20px | |
| 48 | +| xl | 28px | 24px | |
| 49 | +| 2xl | 32px | 28px | |
| 50 | +| 3xl | 37px | 32px | |
| 51 | +| 4xl | 42px | 37px | |
| 52 | +| 5xl | 48px | 42px | |
| 53 | +| 6xl | 56px | 48px | |
| 54 | + |
| 55 | +<iframe |
| 56 | + class="sb-iframe" |
| 57 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--explicit-sizes" |
| 58 | + width="100%" |
| 59 | + height="136" |
| 60 | + frameborder="1" |
| 61 | +></iframe> |
| 62 | +
|
| 63 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--explicit-sizes) |
| 64 | + |
| 65 | +### Auto-sizing with Typography |
| 66 | + |
| 67 | +When placed inside a Typography component, icons automatically inherit the correct size via the `--eds-typography-icon-size` CSS variable. This is the recommended approach for icons inline with text. |
| 68 | + |
| 69 | +<iframe |
| 70 | + class="sb-iframe" |
| 71 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--auto-size-from-typography" |
| 72 | + width="100%" |
| 73 | + height="308" |
| 74 | + frameborder="1" |
| 75 | +></iframe> |
| 76 | +
|
| 77 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--auto-size-from-typography) |
| 78 | + |
| 79 | +### Density modes |
| 80 | + |
| 81 | +Icons respect the `data-density` attribute for density-aware sizing. The same `size` prop renders at different pixel values depending on the density context. |
| 82 | + |
| 83 | +- **Spacious** (default) - larger icons for more breathing room |
| 84 | +- **Comfortable** - smaller icons for compact UIs |
| 85 | + |
| 86 | +<iframe |
| 87 | + class="sb-iframe" |
| 88 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--density-modes" |
| 89 | + width="100%" |
| 90 | + height="162" |
| 91 | + frameborder="1" |
| 92 | +></iframe> |
| 93 | +
|
| 94 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--density-modes) |
| 95 | + |
| 96 | +### Default sizing |
| 97 | + |
| 98 | +When no `size` prop is set and the icon is outside a Typography component, it scales to 1.5× the surrounding font size. This provides a sensible default in any context. |
| 99 | + |
| 100 | +<iframe |
| 101 | + class="sb-iframe" |
| 102 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--dynamic-fallback" |
| 103 | + width="100%" |
| 104 | + height="251" |
| 105 | + frameborder="1" |
| 106 | +></iframe> |
| 107 | +
|
| 108 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--dynamic-fallback) |
| 109 | + |
| 110 | +### Color |
| 111 | + |
| 112 | +Icons inherit `currentColor` by default, making them adapt to text color automatically. You can also set a custom color using the `color` prop. |
| 113 | + |
| 114 | +<iframe |
| 115 | + class="sb-iframe" |
| 116 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--color" |
| 117 | + width="100%" |
| 118 | + height="88" |
| 119 | + frameborder="1" |
| 120 | +></iframe> |
| 121 | +
|
| 122 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--color) |
| 123 | + |
| 124 | +### Inline text vs flex layout |
| 125 | + |
| 126 | +How you use Icon depends on the layout context: |
| 127 | + |
| 128 | +| Context | Usage | Why | |
| 129 | +| ----------- | ------------------------------ | --------------------------------------------------------------- | |
| 130 | +| Inline text | `<Icon data={save} />` | Auto-sizes to 1.5em, negative margins for optical alignment | |
| 131 | +| Flex/Button | `<Icon data={save} size="md">` | Fixed size from tokens, no margins - layout controlled by flex gap | |
| 132 | + |
| 133 | +The `size` prop removes negative margins, giving full control to your layout system. |
| 134 | + |
| 135 | +<iframe |
| 136 | + class="sb-iframe" |
| 137 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--inline-with-text" |
| 138 | + width="100%" |
| 139 | + height="257" |
| 140 | + frameborder="1" |
| 141 | +></iframe> |
| 142 | +
|
| 143 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--inline-with-text) |
| 144 | + |
| 145 | +## Usage in Figma |
| 146 | + |
| 147 | +There is no dedicated Icon component in Figma. Instead, icons are imported directly from the **Assets** panel. |
| 148 | + |
| 149 | +**Standalone icons** — Import the icon from Assets and place it directly in your design. The icon size is controlled by the asset itself. |
| 150 | + |
| 151 | +**Inline with Typography** — When placing an icon inline with text, wrap it in a container like "Leading Icon Item". This container applies negative padding to optically align the icon with the text baseline — matching how the code component behaves with negative margins for inline icons. |
| 152 | + |
| 153 | +The container exposes variables for controlling size and negative padding: |
| 154 | + |
| 155 | +| Variable | Purpose | |
| 156 | +| ---------------------------------------- | -------------------------------------------- | |
| 157 | +| `Selectable/Icon size` | Controls the icon dimensions | |
| 158 | +| `Selectable/Icon container padding ↕︎` | Negative vertical padding for optical alignment | |
| 159 | +| `Selectable/Icon container padding ↔︎` | Negative horizontal padding (square containers only) | |
| 160 | + |
| 161 | +**Which padding variables to use depends on the container shape:** |
| 162 | + |
| 163 | +- **Rectangular containers** (e.g., a button with text) — Only vertical padding (`↕︎`) is needed. Horizontal spacing is handled by the parent layout's gap. |
| 164 | +- **Square containers** (e.g., an icon-only button) — Both vertical (`↕︎`) and horizontal (`↔︎`) padding are needed, set to the same value, since the icon fills the container in both directions. |
| 165 | + |
| 166 | +## Accessibility |
| 167 | + |
| 168 | +Icons follow WCAG 2.1 AA guidelines with two distinct modes: |
| 169 | + |
| 170 | +**Decorative icons** (no `title` prop) |
| 171 | + |
| 172 | +- Rendered with `aria-hidden="true"` |
| 173 | +- Hidden from screen readers entirely |
| 174 | +- Use when the icon is accompanied by visible text that conveys the same meaning |
| 175 | + |
| 176 | +**Semantic icons** (with `title` prop) |
| 177 | + |
| 178 | +- Rendered with `role="img"` and `aria-labelledby` |
| 179 | +- Announced by screen readers using the title text |
| 180 | +- Use when the icon conveys meaning that is not available through surrounding text |
| 181 | + |
| 182 | +<iframe |
| 183 | + class="sb-iframe" |
| 184 | + src="https://storybook.eds.equinor.com/iframe.html?globals=&args=&id=eds-2-0-beta-icon--accessibility" |
| 185 | + width="100%" |
| 186 | + height="197" |
| 187 | + frameborder="1" |
| 188 | +></iframe> |
| 189 | +
|
| 190 | +[View in Storybook](https://storybook.eds.equinor.com/?path=/story/eds-2-0-beta-icon--accessibility) |
| 191 | + |
| 192 | +## Do's and Don'ts |
| 193 | + |
| 194 | +:::info **Do** |
| 195 | + |
| 196 | +- Use `title` prop when an icon is the only element conveying meaning (e.g., icon-only buttons) |
| 197 | +- Let icons inherit size from Typography when used inline with text |
| 198 | +- Use the `size` prop in flex layouts where you control spacing with gap |
| 199 | +- Rely on `currentColor` for icons that should match surrounding text color |
| 200 | +::: |
| 201 | + |
| 202 | +:::danger **Don't** |
| 203 | + |
| 204 | +- Don't use icons without a `title` when there is no accompanying text to convey the meaning |
| 205 | +- Don't combine the `size` prop with Typography auto-sizing. The `size` prop takes priority and overrides the inherited size, so the Typography context has no effect |
| 206 | +- Don't use custom pixel values for sizing - use the `size` prop or let tokens handle it |
| 207 | +::: |
0 commit comments