diff --git a/src/components/accordion.scss b/src/components/accordion.scss index d8795b78..99d346df 100644 --- a/src/components/accordion.scss +++ b/src/components/accordion.scss @@ -17,9 +17,8 @@ } .accordion__marker { - --op-mso-optical-sizing: 48; + @extend %icon-x-large-global; - font-size: var(--op-font-x-large); justify-self: flex-end; transition: var(--op-transition-accordion); user-select: none; diff --git a/src/components/alert.scss b/src/components/alert.scss index 3b69cfd9..23117141 100644 --- a/src/components/alert.scss +++ b/src/components/alert.scss @@ -6,10 +6,8 @@ gap: var(--op-space-small); .alert__icon { - /* stylelint-disable scss/at-extend-no-missing-placeholder */ - @extend .icon--large; - @extend .icon--weight-bold; - /* stylelint-enable scss/at-extend-no-missing-placeholder */ + @extend %icon-large-global; + @extend %icon-weight-bold-global; line-height: var(--op-line-height-dense); } diff --git a/src/components/badge.scss b/src/components/badge.scss index 539cacb0..a596b44a 100644 --- a/src/components/badge.scss +++ b/src/components/badge.scss @@ -23,7 +23,7 @@ white-space: nowrap; .material-symbols-outlined { - font-size: var(--op-font-small); + @extend %icon-small-global; } &.badge--pill { diff --git a/src/components/icon.scss b/src/components/icon.scss index 9fbacd9e..eefd9e26 100644 --- a/src/components/icon.scss +++ b/src/components/icon.scss @@ -5,89 +5,151 @@ @import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200'; -.material-symbols-outlined, -.material-symbols { - --op-mso-fill: 0; - --op-mso-weight: var(--op-font-weight-normal); - --op-mso-grade: 0; - --op-mso-optical-sizing: 20; - - font-size: var(--op-font-medium); - font-variation-settings: 'FILL' var(--op-mso-fill), 'wght' var(--op-mso-weight), 'GRAD' var(--op-mso-grade), - 'opsz' var(--op-mso-optical-sizing); - line-height: var(--op-line-height-densest); - vertical-align: middle; +%icon-small-global { + --__op-icon-font-size: var(--_op-icon-font-size-small); + --__op-icon-optical-size: var(--_op-icon-optical-size-small); } -// Fill -.icon--outlined { - --op-mso-fill: 0; +%icon-medium-global { + --__op-icon-font-size: var(--_op-icon-font-size-medium); + --__op-icon-optical-size: var(--_op-icon-optical-size-medium); } -.icon--filled { - --op-mso-fill: 1; +%icon-large-global { + --__op-icon-font-size: var(--_op-icon-font-size-large); + --__op-icon-optical-size: var(--_op-icon-optical-size-large); } -// Weight -.icon--weight-light { - --op-mso-weight: var(--op-font-weight-light); +%icon-x-large-global { + --__op-icon-font-size: var(--_op-icon-font-size-x-large); + --__op-icon-optical-size: var(--_op-icon-optical-size-x-large); } -.icon--weight-normal { - --op-mso-weight: var(--op-font-weight-normal); +%icon-weight-light-global { + --__op-icon-weight: var(--_op-icon-weight-light); } -.icon--weight-semi-bold { - --op-mso-weight: var(--op-font-weight-semi-bold); +%icon-weight-normal-global { + --__op-icon-weight: var(--_op-icon-weight-normal); } -.icon--weight-bold { - --op-mso-weight: var(--op-font-weight-bold); +%icon-weight-semi-bold-global { + --__op-icon-weight: var(--_op-icon-weight-semi-bold); } -// Emphasis -.icon--low-emphasis { - --op-mso-grade: -20; +%icon-weight-bold-global { + --__op-icon-weight: var(--_op-icon-weight-bold); } -.icon--normal-emphasis { - --op-mso-grade: 0; -} +%icon-global { + // Public API (allowed to be overridden) + // Weight + --_op-icon-weight-light: var(--op-font-weight-light); + --_op-icon-weight-normal: var(--op-font-weight-normal); + --_op-icon-weight-semi-bold: var(--op-font-weight-semi-bold); + --_op-icon-weight-bold: var(--op-font-weight-bold); -.icon--high-emphasis { - --op-mso-grade: 200; -} + // Fill + --_op-icon-fill-outlined: 0; + --_op-icon-fill-filled: 1; -// Size -.icon--medium { - font-size: var(--op-font-medium); + // Emphasis + --_op-icon-emphasis-low: -20; + --_op-icon-emphasis-normal: 0; + --_op-icon-emphasis-high: 200; - --op-mso-optical-sizing: 20; -} + // Size + --_op-icon-font-size-small: var(--op-font-small); + --_op-icon-font-size-medium: var(--op-font-medium); + --_op-icon-font-size-large: var(--op-font-large); + --_op-icon-font-size-x-large: var(--op-font-2x-large); + --_op-icon-optical-size-small: 20; + --_op-icon-optical-size-medium: 20; + --_op-icon-optical-size-large: 40; + --_op-icon-optical-size-x-large: 48; -.icon--large { - font-size: var(--op-font-large); + // Private API (don't touch these) + --__op-icon-weight: var(--_op-icon-weight-normal); + --__op-icon-fill: var(--_op-icon-fill-outlined); + --__op-icon-emphasis: var(--_op-icon-emphasis-normal); + --__op-icon-font-size: var(--_op-icon-font-size-medium); + --__op-icon-optical-size: var(--_op-icon-optical-size-medium); - --op-mso-optical-sizing: 40; -} + font-size: var(--__op-icon-font-size); + font-variation-settings: 'FILL' var(--__op-icon-fill), 'wght' var(--__op-icon-weight), + 'GRAD' var(--__op-icon-emphasis), 'opsz' var(--__op-icon-optical-size); + line-height: var(--op-line-height-densest); + vertical-align: middle; + + // Fill Modifiers + &.icon--outlined { + --__op-icon-fill: var(--_op-icon-fill-outlined); + } + + &.icon--filled { + --__op-icon-fill: var(--_op-icon-fill-filled); + } + + // Weight Modifiers + &.icon--weight-light { + @extend %icon-weight-light-global; + } + + &.icon--weight-normal { + @extend %icon-weight-normal-global; + } + + &.icon--weight-semi-bold { + @extend %icon-weight-semi-bold-global; + } + + &.icon--weight-bold { + @extend %icon-weight-bold-global; + } + + // Emphasis + &.icon--low-emphasis { + --__op-icon-emphasis: var(--_op-icon-emphasis-low); + } + + &.icon--normal-emphasis { + --__op-icon-emphasis: var(--_op-icon-emphasis-normal); + } + + &.icon--high-emphasis { + --__op-icon-emphasis: var(--_op-icon-emphasis-high); + } + + // Size Modifiers + &.icon--small { + @extend %icon-small-global; + } + + &.icon--medium { + @extend %icon-medium-global; + } -.icon--x-large { - font-size: var(--op-font-2x-large); + &.icon--large { + @extend %icon-large-global; + } - --op-mso-optical-sizing: 48; + &.icon--x-large { + @extend %icon-x-large-global; + } +} + +.material-symbols-outlined, +.material-symbols { + @extend %icon-global; } .custom-icons { + @extend %icon-global; + display: inline-block; width: var(--op-font-2x-large); height: var(--op-font-2x-large); - &.icon--medium { - font-size: var(--op-font-small); - - --op-mso-optical-sizing: 20; - } - &.icon--large { width: var(--op-font-large); height: var(--op-font-large); diff --git a/src/components/sidebar.scss b/src/components/sidebar.scss index eb6d69de..7f7f7fde 100644 --- a/src/components/sidebar.scss +++ b/src/components/sidebar.scss @@ -101,10 +101,8 @@ justify-content: flex-start; .material-symbols-outlined { - /* stylelint-disable scss/at-extend-no-missing-placeholder */ - @extend .icon--large; - @extend .icon--weight-bold; - /* stylelint-enable scss/at-extend-no-missing-placeholder */ + @extend %icon-large-global; + @extend %icon-weight-bold-global; } } diff --git a/src/stories/Components/Icon/Icon.js b/src/stories/Components/Icon/Icon.js index a53f7fb0..eb52b933 100644 --- a/src/stories/Components/Icon/Icon.js +++ b/src/stories/Components/Icon/Icon.js @@ -4,10 +4,10 @@ export const createIcon = ({ name, filled = false, size = 'medium', weight = 'no icon.className = [ 'material-symbols-outlined', - filled ? 'icon--filled' : 'icon--outlined', - `icon--${size}`, - `icon--weight-${weight}`, - `icon--${emphasis}-emphasis`, + filled ? 'icon--filled' : '', + size !== 'medium' ? `icon--${size}` : '', + weight !== 'normal' ? `icon--weight-${weight}` : '', + emphasis !== 'normal' ? `icon--${emphasis}-emphasis` : '', ] .filter(Boolean) .join(' ') diff --git a/src/stories/Components/Icon/Icon.mdx b/src/stories/Components/Icon/Icon.mdx index e9b7f6ba..974345ac 100644 --- a/src/stories/Components/Icon/Icon.mdx +++ b/src/stories/Components/Icon/Icon.mdx @@ -45,7 +45,7 @@ To use an icon, put an element (usually a `span`) with the class of `.material-s ### Size -`.icon--medium`, `.icon--large`, `.icon--x-large` (with medium being the default) modify the size of any icon. +`.icon--small`, `.icon--medium`, `.icon--large`, `.icon--x-large` (with medium being the default) modify the size of any icon. @@ -62,3 +62,89 @@ Emphasis acts similarly to weight, but changes the thickness of the icon strokes `.icon--low-emphasis`, `.icon--normal-emphasis`, `.icon--high-emphasis` (with normal being the default) modify the emphasis of any icon. + +## Icon API + +Each icon modifier works off of a scale for each variation. These can be overriden to change how icons scale in your application. + +{/* prettier-ignore-start */} +```css +// Public API (allowed to be overridden) +// Weight +--_op-icon-weight-light: +--_op-icon-weight-normal: +--_op-icon-weight-semi-bold: +--_op-icon-weight-bold: + +// Fill +--_op-icon-fill-outlined: +--_op-icon-fill-filled: + +// Emphasis +--_op-icon-emphasis-low: +--_op-icon-emphasis-normal: +--_op-icon-emphasis-high: + +// Size +--_op-icon-font-size-small: +--_op-icon-font-size-medium: +--_op-icon-font-size-large: +--_op-icon-font-size-x-large: +--_op-icon-optical-size-small: +--_op-icon-optical-size-medium: +--_op-icon-optical-size-large: +--_op-icon-optical-size-x-large: +``` +{/* prettier-ignore-end */} + +## Overriding Styles + +The icon classes are built on a [sass placeholder selector](https://sass-lang.com/documentation/style-rules/placeholder-selectors) + +Icon takes advantage of multiple selector placeholders to allow for easy overriding of styles. They can be overridden to change all icon styles: + +```css +%icon-small-global { +} + +%icon-medium-global { +} + +%icon-large-global { +} + +%icon-x-large-global { +} + +%icon-weight-light-global { +} + +%icon-weight-normal-global { +} + +%icon-weight-semi-bold-global { +} + +%icon-weight-bold-global { +} + +%icon-global { +} +``` + +Sometimes other components need to modify what icons look like within then. Rather than having to ensure the right size modifier class is used when using the parent component, the parent component can force the size using the size global modifiers. Examples from accordion and alert looks like: + +```css +.accordion__marker { + @extend %icon-x-large-global; + + /* ... */ +} + +.alert__icon { + @extend %icon-large-global; + @extend %icon-weight-bold-global; + + /* ... */ +} +``` diff --git a/src/stories/Components/Icon/Icon.stories.js b/src/stories/Components/Icon/Icon.stories.js index 3504ddbf..123c4158 100644 --- a/src/stories/Components/Icon/Icon.stories.js +++ b/src/stories/Components/Icon/Icon.stories.js @@ -10,7 +10,7 @@ export default { filled: { control: 'boolean' }, size: { control: { type: 'select' }, - options: ['normal', 'large', 'x-large'], + options: ['small', 'medium', 'large', 'x-large'], }, weight: { control: { type: 'select' },