diff --git a/addons/rose/addon/styles/rose/_index.scss b/addons/rose/addon/styles/rose/_index.scss index ab921a668..b3ddfcdd1 100644 --- a/addons/rose/addon/styles/rose/_index.scss +++ b/addons/rose/addon/styles/rose/_index.scss @@ -1,3 +1,4 @@ @import 'reset'; @import 'variables'; +@import 'typography'; @import 'components'; diff --git a/addons/rose/addon/styles/rose/_typography.scss b/addons/rose/addon/styles/rose/_typography.scss new file mode 100644 index 000000000..abf068ab1 --- /dev/null +++ b/addons/rose/addon/styles/rose/_typography.scss @@ -0,0 +1,45 @@ +@use 'variables/typography'; +@use 'variables/sizing'; +@use 'utilities/type' as type-mixins; + +html { + -webkit-font-smoothing: antialiased; +} + +body { + font-family: typography.$family-primary; + // This is the only time we'll explicitly use pixels. All other sizes are + // expressed in rem units. + font-size: typography.$base-font-size * 1px; + line-height: typography.line-height(m); +} + +pre, +code { + font-family: typography.$family-monospace; +} + +h1, +.h1 { + @include type-mixins.type(h1, bold); + margin-top: sizing.rems(m); + margin-bottom: sizing.rems(xs); +} + +h2, +.h2 { + @include type-mixins.type(h2, bold); + margin-top: sizing.rems(m); + margin-bottom: sizing.rems(xs); +} + +h3, +.h3 { + @include type-mixins.type(h3, bold); + margin-top: sizing.rems(m); + margin-bottom: sizing.rems(xs); +} + +p { + margin-bottom: sizing.rems(m); +} diff --git a/addons/rose/addon/styles/rose/components/_button.scss b/addons/rose/addon/styles/rose/components/_button.scss index 472e02563..a043f2e62 100644 --- a/addons/rose/addon/styles/rose/components/_button.scss +++ b/addons/rose/addon/styles/rose/components/_button.scss @@ -1,4 +1,11 @@ @use '../utilities/button'; +@use '../utilities/type' as typography; +@use '../variables/sizing'; + +$size-xs: sizing.rems(xs); // 8 +$size-xxs: sizing.rems(xxs); // 4 +$size-xxxs: sizing.rems(xxxs); // 2 +$size-xxxxs: sizing.rems(xxxxs); // 1 @include button.button-style( 'primary', @@ -6,14 +13,14 @@ color: var(--white), background-color: var(--info), border-color: var(--info-starker-2), - box-shadow: 0px 4px 4px rgba(var(--stark-components), var(--opacity-2)), - text-shadow: 0px 1px 1px rgba(var(--black-components), var(--opacity-2)), + box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)), + text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)), ), ( background-color: var(--info-subtler-1), box-shadow: ( - 0px 4px 4px rgba(var(--stark-components), var(--opacity-3)), - 0px 4px 8px rgba(var(--stark-components), var(--opacity-4)), + 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)), + 0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)), ), ), ( @@ -32,15 +39,15 @@ color: var(--ui-gray-starker-3), background-color: var(--ui-gray-subtler-5), border-color: var(--ui-gray-subtler-2), - box-shadow: 0px 4px 4px rgba(var(--stark-components), var(--opacity-2)), + box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)), ), ( color: var(--ui-gray-starker-4), background-color: var(--subtle), border-color: var(--ui-gray-starker-1), box-shadow: ( - 0px 4px 4px rgba(var(--stark-components), var(--opacity-3)), - 0px 4px 8px rgba(var(--stark-components), var(--opacity-4)), + 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)), + 0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)), ), ), ( @@ -61,27 +68,27 @@ color: var(--failure), background-color: var(--subtle), border-color: var(--failure), - box-shadow: 0px 4px 4px rgba(var(--stark-components), var(--opacity-2)), + box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)), ), ( color: var(--white), border-color: var(--failure-starker-1), background-color: var(--failure), box-shadow: ( - 0px 4px 4px rgba(var(--stark-components), var(--opacity-3)), - 0px 4px 8px rgba(var(--stark-components), var(--opacity-4)), + 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)), + 0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)), ), - text-shadow: 0px 1px 1px rgba(var(--black-components), var(--opacity-2)), + text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)), ), ( color: var(--white), border-color: var(--ui-gray-starker-2), background-color: var(--failure-starker-1), box-shadow: ( - 0px 4px 4px rgba(var(--stark-components), var(--opacity-3)), - 0px 4px 8px rgba(var(--stark-components), var(--opacity-4)), + 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)), + 0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)), ), - text-shadow: 0px 1px 1px rgba(var(--black-components), var(--opacity-2)), + text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)), ), ( border-color: var(--ui-gray-subtler-2), @@ -109,15 +116,13 @@ ); .rose-button-inline-link-action { + @include typography.type(m, semibold); color: var(--info); border: 0; background: none; - cursor: pointer; margin: 0; padding: 0; display: inline; - font-size: 1rem; - font-weight: 600; &:not(:disabled) { &:hover { diff --git a/addons/rose/addon/styles/rose/utilities/_button.scss b/addons/rose/addon/styles/rose/utilities/_button.scss index 42ca267fc..29cee9130 100644 --- a/addons/rose/addon/styles/rose/utilities/_button.scss +++ b/addons/rose/addon/styles/rose/utilities/_button.scss @@ -1,14 +1,14 @@ @use 'sass:map'; +@use 'type' as typography; +@use '../variables/sizing'; @mixin button { + @include typography.type(button, semibold); display: flex; flex-direction: row; - padding: 5px 24px; - line-height: 1.846; // targeting 24px - border-radius: 2px; - border: 1px solid; - font-size: 0.8125rem; - font-weight: 600; + padding: sizing.rems(off-xs) sizing.rems(l); + border-radius: sizing.rems(xxxs); + border: sizing.rems(xxxxs) solid; } @mixin button-style($name, $default, $hover: &, $active: &, $disabled: &) { diff --git a/addons/rose/addon/styles/rose/utilities/_type.scss b/addons/rose/addon/styles/rose/utilities/_type.scss new file mode 100644 index 000000000..e689768a4 --- /dev/null +++ b/addons/rose/addon/styles/rose/utilities/_type.scss @@ -0,0 +1,13 @@ +@use '../variables/typography'; + +/** + * Mixin for specifying a font-size/line-height size on an element, with + * an option for specifying weight. + */ +@mixin type($size, $weight: &) { + font-size: typography.font-size($size); + line-height: typography.line-height($size); + @if $weight { + font-weight: typography.font-weight($weight); + } +} diff --git a/addons/rose/addon/styles/rose/variables/_index.scss b/addons/rose/addon/styles/rose/variables/_index.scss index 90dda5a07..0f1c38707 100644 --- a/addons/rose/addon/styles/rose/variables/_index.scss +++ b/addons/rose/addon/styles/rose/variables/_index.scss @@ -1,2 +1,4 @@ +@import 'sizing'; +@import 'typography'; @import 'color'; @import 'opacity'; diff --git a/addons/rose/addon/styles/rose/variables/_sizing.scss b/addons/rose/addon/styles/rose/variables/_sizing.scss new file mode 100644 index 000000000..ac73d8fae --- /dev/null +++ b/addons/rose/addon/styles/rose/variables/_sizing.scss @@ -0,0 +1,32 @@ +@use 'sass:map'; + +/** + * SASS variables for size and scale. + */ + +$size-coefficients: ( + xxl: 3, // 48 + xl: 2.25, // 36 + l: 1.5, // 24 + m: 1, // 16 - base + s: 0.75, // 12 + xs: 0.5, // 8 + off-xs:0.3125, // 5 + xxs: 0.25, // 4 + xxxs: 0.125, // 2 + xxxxs: 0.0625 // 1 +); + +/** + * Returns a size coefficient. + */ +@function size($name) { + @return map.get($size-coefficients, $name); +} + +/** + * Returns a value in rem units equal to the requested size. + */ +@function rems($name) { + @return size($name) * 1rem; +} diff --git a/addons/rose/addon/styles/rose/variables/_typography.scss b/addons/rose/addon/styles/rose/variables/_typography.scss new file mode 100644 index 000000000..e17769890 --- /dev/null +++ b/addons/rose/addon/styles/rose/variables/_typography.scss @@ -0,0 +1,89 @@ +@use 'sass:map'; +@use 'sizing'; + +/** + * SASS variables defining typography. + */ + +$family-primary: + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + 'Roboto', + 'Oxygen', + 'Ubuntu', + 'Cantarell', + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + +$family-monospace: + 'SFMono-Regular', + Consolas, + 'Liberation Mono', + Menlo, + Courier, + monospace; + +// Available font weights by name +$font-weights: ( + normal: 400, + semibold: 600, + bold: 700 +); + +// unitless value to start, but think pixels or points +$base-font-size: 16; + +// Font sizes don't quite match up with the size scale, so they're recalculated. +$font-sizes: ( + xl: sizing.size(m) * 1.75, // 28 + l: sizing.size(m) * 1.25, // 20 + m: sizing.size(m), // 16 + s: sizing.size(m) * 0.875, // 14 + xs: sizing.size(m) * 0.75, // 12 + + h1: sizing.size(m) * 1.75, // 28 + h2: sizing.size(m) * 1.25, // 20 + h3: sizing.size(m), // 16 + + button: sizing.size(m) * 0.8125 // 13 +); + +// Line height expressed as ratio. +$line-heights: ( + xl: 36 / 28, + l: 24 / 20, + m: 20 / 16, + s: 18 / 14, + xs: 16 / 12, + + h1: 48 / 28, + h2: 36 / 20, + h3: 24 / 16, + + button: 24 / 13 +); + +/** + * Returns a named font size in rem units. + */ +@function font-size($name) { + @return map.get($font-sizes, $name) * 1rem; +} + +/** + * Returns a named line height as a unitless value. + */ +@function line-height($name) { + @return map.get($line-heights, $name); +} + +/** + * Returns a named font weight. + */ +@function font-weight($name) { + @return map.get($font-weights, $name); +} diff --git a/addons/rose/stories/style-guide.stories.js b/addons/rose/stories/style-guide.stories.js index bfab579a8..c7bfc8dd2 100644 --- a/addons/rose/stories/style-guide.stories.js +++ b/addons/rose/stories/style-guide.stories.js @@ -9,23 +9,9 @@ export const Colors = () => ({ +
+

Sizing

+

+ Use sizing function for things like margins, + padding, and borders. Sizes are returned in rem units rather than + pixels, but have been calibrated with pixel equivalents in mind. +

+

+ To use the rems sizing function, first import the sizing module, + then use it in your code wherever you normally specify a size: +

+
+@import "variables/sizing";
+border: sizing.rems(xxxxs) solid;
+ + + + + + + + + + {{#each sizes as |size|}} + + + + + + {{/each}} + +
NamePX Equiv.SCSS
+ {{size.code}} + {{size.pixels}} + sizing.rems({{size.code}}) +
+
+ `, + context: { + sizes: [ + {code: 'xxl', pixels: 48}, + {code: 'xl', pixels: 36}, + {code: 'l', pixels: 24}, + {code: 'm', pixels: 16, isBase: true}, + {code: 's', pixels: 12}, + {code: 'xs', pixels: 8}, + {code: 'off-xs', pixels: 5}, + {code: 'xxs', pixels: 4}, + {code: 'xxxs', pixels: 2}, + {code: 'xxxxs', pixels: 1}, + ] + } +}); + +export const Typography = () => ({ + template: hbs` + +
+

Typography

+

+ Paragraphs and headings are preconfigured for typography and margin. +

+

+ To specify custom typography, use the SCSS variables and mixin. + The type mixin accepts two parameters: a font size name + (required) and an optional font weight name (normal, semibold, bold). + This mixin sets both font size and line height, and optionally weight. +

+
+@import "utilities/type";
+.my-component {
+  @include type.type(m, bold);
+}
+ + + + + + + + + + + {{#each sizes as |size|}} + + + + + + + {{/each}} + +
NameFont SizeSCSSNote
+ {{size.code}} + {{size.pixels}} + @include type.type({{size.code}}); + + {{size.note}} +
+
+ `, + context: { + sizes: [ + {code: 'xl', pixels: 28}, + {code: 'l', pixels: 20}, + {code: 'm', pixels: 16}, + {code: 's', pixels: 14}, + {code: 'xs', pixels: 12}, + {code: 'h1', pixels: 28, note: 'Same as xl, but different line height.'}, + {code: 'h2', pixels: 20, note: 'Same as l, but different line height.'}, + {code: 'h3', pixels: 16, note: 'Same as m, but different line height.'}, + {code: 'button', pixels: 13} + ] + } +});