Glide CSS

A tiny component-first CSS framework for Sass; with a zero footprint by default. Build solid design systems with a set of simple tools, and the power to generate utility classes where appropriate.

Usage

Themes

At the heart of Glide is the ability to define a free-form theme.

SCSS
@import "glidecss/base";

@include theme((
  palette: (
    primary: (
      1: #fff,
      2: #eee,
      3: #888
    ),
    accent: (
      1: #e70,
      2: #f80,
      3: #f91
    )
  ),
  spacing: (
    1: .25rem,
    2: .5rem,
    3: .75rem
  )
));

This is a basic theme which can be accessed by using the theme function.

SCSS
.button {
  padding: theme(spacing, 3);
  color: theme(palette, primary, 1);
  background: theme(palette, accent, 2);

  &:hover {
    background: theme(palette, accent, 3);
  }
}
CSS
.button {
  padding: .75rem;
  color: #fff;
  background: #f80;
}

.button:hover {
  background: #f91;
}

Defaults

A default property can be defined within a set of properties, via the DEFAULT property key.

SCSS
@include theme((
  radius: (
    DEFAULT: .25rem,
    large: .5rem
  )
));

The default is returned when requesting the parent property, so the following are equivilent.

SCSS
border-radius: theme(radius);            // <- .25rem
border-radius: theme(radius, DEFAULT);   // <- .25rem

Overrides

The theme mixin performs a non-destrutive merge — in a similar fashion to the !default operator in Sass — so multiple calls will only add what’s new.

SCSS
@include theme((
  radius: (
    large: .5rem
  )
));

@include theme((
  radius: (
    small: .125rem,
    large: 1rem
  )
));

This is equivilent to the following, as the original large property is preserved.

SCSS
@include theme((
  radius: (
    small: .125rem,
    large: .5rem
  )
));

As you can see, this means that overrides should be defined before futher theme declarations. More on why this is powerful in the dedicated overrides section.

Components

Global theme properties are powerful when combined with component theme properties.

SCSS
@include theme((
  menu: (
    background: theme(palette, primary, 2)
    padding: theme(spacing, 2),
    item: (
      display: inline-block
    )
  )
));

.menu {
  background: theme(menu, background);
  padding: theme(menu, padding);
}

.menu__item {
  display: theme(menu, item, display);
}
CSS
.menu {
  background: #eee;
  padding: .5rem
}

.menu__item {
  display: inline-block;
}

Here our component properties make use of global theme properties, keeping our styling DRY (Don’t Repeat Yourself).

Screens

Glide is mobile first by default, so you can specify styles that trigger for, and above, a given screen size. See configuration section for how to set screen sizes.

Manual

Use screen to manually define styles for a named screen.

SCSS
.menu {
  padding: theme(spacing, 2);

  @include screen(lg) {
    padding: theme(spacing, 3);
  }
}
CSS
.menu {
  padding: .5rem;
}

@media (min-width: 1024px) {
  .menu {
    padding: .75rem;
  }
}

Lookup

We can make a component responsive by defining properties that trigger for various screens.

SCSS
@include theme((
  menu: (
    background: theme(palette, primary, 2),
    padding: (
      DEFAULT: theme(spacing, 2),
      lg: theme(spacing, 3)
    ),
    item: (
      display: (
        DEFAULT: block,
        md: inline-block
      )
    )
  )
));

.menu {
  background: theme(menu, palette, background);

  @include screens(menu, padding) using ($padding) {
    padding: $padding;
  }
}

.menu__item {
  @include screens(menu, item, display) using ($display) {
    display: $display;
  }
}

Responsive properties are named after their screen name. The DEFAULT will be defined first, as the fallback for all screens.

CSS
.menu {
  background: #eee;
  padding: .5rem;
}

@media (min-width: 1024px) {
  .menu {
    padding: .75rem;
  }
}

.menu__item {
  display: block;
}

@media (min-width: 768px) {
  .menu__item {
    display: inline-block;
  }
}

Lookup (shorthand)

It can be cumbersome to write out the longhand block for a single property, so there is a shorthand that takes a property and a path list:

SCSS
##### SCSS

```scss
@include theme((
  menu: (
    padding: (
      DEFAULT: theme(spacing, 2),
      lg: theme(spacing, 3)
    )
  )
));

.menu {
  @include screens(padding, (menu, padding));
}
CSS
.menu {
  padding: .5rem;
}

@media (min-width: 1024px) {
  .menu {
    padding: .75rem;
  }
}

Inline

You can supply a map to screens, although property lookup is recommended.

SCSS
.menu {
  @include screens((
    DEFAULT: theme(spacing, 2),
    lg: theme(spacing, 3)
  )) using ($padding) {
    padding: $padding;
  }
}
CSS
.menu {
  padding: .5rem;
}

@media (min-width: 1024px) {
  .menu {
    padding: .75rem;
  }
}

Inline (shorthand)

It can be cumbersome to write out the longhand block for a single property, so there is a shorthand that takes a property and an inline map:

SCSS
##### SCSS

```scss
.menu {
  @include screens(padding, (DEFAULT: theme(spacing, 2), lg: theme(spacing, 3)));
}
CSS
.menu {
  padding: .5rem;
}

@media (min-width: 1024px) {
  .menu {
    padding: .75rem;
  }
}

Configure

To configure the screen sizes available, just add a screens property to the theme.

SCSS
@include theme((
  screens: (
    custom: 800px
  )
));

.button {
  @include screen(custom) {
    padding: theme(spacing, 3);
  }
}
CSS
@media (min-width: 800px) {
  .button {
    padding: .75rem;
  }
}

Modes

You can specify styles that should be used for a given mode, for example dark mode. By default, a mode is triggered via a parent class named after the mode; usually on the html element.

Manual

Use mode to manually define styles for the named mode.

SCSS
.menu {
  background: theme(palette, primary, 2);

  @include mode(dark) {
    background: theme(palette, primary, 3)
  }
}
CSS
.menu {
  background: #eee;
}

.dark .menu {
  background: #888;
}

Lookup

To easily set properties across modes, we can make use of modes to look them up.

SCSS
@include theme((
  menu: (
    background: (
      DEFAULT: theme(palette, primary, 2),
      dark: theme(palette, primary, 3)
    )
  )
));

.menu {
  @include modes(menu, background) using ($background) {
    background: $background;
  }
}
CSS
.menu {
  background: #eee;
}

.dark .menu {
  background: #888;
}

Lookup (shorthand)

It can be cumbersome to write out the longhand block for a single property, so there is a shorthand that takes a property and a path list:

SCSS
@include theme((
  menu: (
    background: (
      DEFAULT: theme(palette, primary, 2),
      dark: theme(palette, primary, 3)
    )
  )
));

.menu {
  @include modes(background, (menu, background));
}
CSS
.menu {
  background: #eee;
}

.dark .menu {
  background: #888;
}

Inline

You can supply a map to modes, although property lookup is recommended.

SCSS
.menu {
  @include modes((
    DEFAULT: theme(palette, primary, 2),
    dark: theme(palette, primary, 3)
  )) using ($background) {
    background: $background;
  }
}
CSS
.menu {
  background: #eee;
}

.dark .menu {
  background: #888;
}

Inline (shorthand)

It can be cumbersome to write out the longhand block for a single property, so there is a shorthand that takes a property and an inline map:

SCSS
.menu {
  @include modes(background, (
    DEFAULT: theme(palette, primary, 2),
    dark: theme(palette, primary, 3)
  ));
}
CSS
.menu {
  background: #eee;
}

.dark .menu {
  background: #888;
}

Configure

To configure the modes available, just add a modes property to the theme in the following format. Each <token> will be replaced with the relevant value.

SCSS
@include theme((
  modes: (
    dimmed: '.<mode> <selector>'
  ),
  palette: (
    primary: (
      1: (
        DEFAULT: #fff,
        dimmed: #444
      )
    )
  )
));

body {
  @include modes(palette, primary, 1) using ($background) {
    background: $background;
  }
}
CSS
body {
  background: #fff;
}

.dimmed body {
  background: #444;
}

Utilities

Glide encourages components where possible, however you can generate utilities where it makes sense. We can use variants and modifiers to generate utility classes.

Variants

Variants allow us to generate utility classes with a prefix. By default, we can generate variants for screen, mode, and any pseudo selector such as hover.

SCSS
.box {
  @include variants(screen, hover) {
    padding: theme(spacing, 4);
  }
}
CSS
.box {
  padding: 1rem;
}

@media (min-width: 640px) {
  .sm\:box {
    padding: 1rem;
  }
}

@media (min-width: 768px) {
  .md\:box {
    padding: 1rem;
  }
}

/* ... */

.hover\:box:hover {
  padding: 1rem;
}

Modifiers

Modifiers allow us to generate utility classes with suffixes, based on a theme property or Sass map.

SCSS
.box {
  @include modifiers(spacing) using ($spacing) {
    padding: $spacing;
  }
}
CSS
.box-1 {
  padding: .25rem;
}

.box-2 {
  padding: .5rem;
}

.box-3 {
  padding: .75rem;
}

/* etc. */

You can also specify an inline map, although property lookup is recommended.

SCSS
.box {
  @include modifiers((1: .25rem, 2: .5rem)) using ($spacing) {
    padding: $spacing;
  }
}
CSS
.box-1 {
  padding: .25rem;
}

.box-2 {
  padding: .5rem;
}

Modifiers (shorthand)

It can be cumbersome to write out the longhand block for a single property, so there is a shorthand that takes a property with a path list or inline map:

SCSS
.box {
  @include modifiers(padding, (spacing));
}
CSS
.box-1 {
  padding: .25rem;
}

.box-2 {
  padding: .5rem;
}

.box-3 {
  padding: .75rem;
}

/* etc. */

You can also specify an inline map, although property lookup is recommended.

SCSS
.box {
  @include modifiers(padding, (1: .25rem, 2: .5rem));
}
CSS
.box-1 {
  padding: .25rem;
}

.box-2 {
  padding: .5rem;
}

Combined

We can combine variants and modifiers to create responsive, and/or mode specific utility classes.

SCSS
.box {
  @include variants(screen, mode) {
    @include modifiers(spacing) using ($spacing) {
      padding: $spacing;
    }
  }
}
CSS
.box-1 {
  padding: .25rem;
}

.box-2 {
  padding: .5rem;
}

/* ... */

@media (min-width: 640px) {
  .sm\:box-1 {
    padding: .25rem;
  }

  .md\:box-2 {
    padding: .5rem;
  }

  /* ... */
}

.dark\:box-1 {
  padding: .25rem;
}

.dark\:box-2 {
  padding: .5rem;
}

/* ... */

Use this power wisely. If you end up with many utility classes — not recommended — then it’s advised that you add purgecss to your project.

Configure

To configure the variants available, just add a variants property to the theme in the following format. Each <token> will be replaced with the relevant value.

SCSS
@include theme((
  variants: (
    first: '<class>:<variant>-child',
    last: '<class>:<variant>-child'
  )
));

.highlight {
  @include variants(first, last) {
    background: theme(palette, accent, 2);
  }
}
CSS
.highlight {
  background: #f80;
}

.first\:highlight:first-child {
  background: #f80;
}

.last\:highlight:last-child {
  background: #f80;
}

Reset

Glide comes with a simple opt-in reset, just import the reset stylesheet.

SCSS
@import "glidecss/reset";

Defaults

Glide provides a set of opt-in defaults. You can include them all, or cherry pick the ones you’d like. To include them all, just import the main defaults file.

SCSS
@include "glidecss/defaults";

Screens

The default screens are shown below.

Screen Min width
sm 640px
md 768px
lg 1024px
xl 1280px
xxl 1536px
SCSS
@include "glidecss/defaults/screens";

body {
  @include screen(md) {
    padding: 1rem;
  }
}
CSS
@media (min-width: 768px) {
  body {
    padding: 1rem;
  }
}

Modes

The default modes are show below.

Mode Value
dark .<mode> <selector>
SCSS
@include "glidecss/defaults/modes";

body {
  @include mode(dark) {
    background: black;
  }
}
CSS
.dark body {
  background: black;
}

Variants

The default variants are show below.

Mode Value
DEFAULT <class>:<variant>
screen (type: screen)
mode (type: mode )
SCSS
@include "glidecss/defaults/variants";

.box {
  @include variants(screen) {
    padding: 1rem
  }
}
CSS
.box {
  padding: 1rem;
}

@media (min-width: 640px) {
  .sm\:box {
    padding: 1rem;
  }
}

/* ... */

Palette

The default palette is accessed with theme(palette, <name>, <tone>, <mode>); color names are semantic, so tweak as necessary to fit your brand.

Primary
Tone Default mode Dark mode
0 tint(#22272f, 0%) shade(#22272f, 90%)
1 tint(#22272f, 35%) shade(#22272f, 70%)
2 tint(#22272f, 65%) shade(#22272f, 40%)
3 tint(#22272f, 85%) shade(#22272f, 20%)
4 tint(#22272f, 95%) shade(#22272f, 10%)
5 tint(#22272f, 100%) tint(#22272f, 0%)
6 tint(#22272f, 95%) tint(#22272f, 10%)
7 tint(#22272f, 85%) tint(#22272f, 20%)
8 tint(#22272f, 65%) tint(#22272f, 40%)
9 tint(#22272f, 35%) tint(#22272f, 70%)
10 tint(#22272f, 0%) tint(#22272f, 90%)

Note the use of supplied tint and shade functions.

SCSS
@include "glidecss/defaults/palette";

body {
  @include modes(palette, primary, 10) using ($color) {
    color: $color
  }

  @include modes(palette, primary, 5) using ($background) {
    background: $background
  }
}
CSS
body {
  color: #22272f;
  background: #fff;
}

.dark body {
  color: #fff;
  background: #22272f;
}

Here 10 works well as a foreground color, and 5 as a background color. This allows you to switch modes, with predictable results.

Spacing

The default spacing values are accessed with theme(spacing, <name>).

Name Size Pixels
1 .25rem 4px
2 .5rem 8px
3 1rem 16px
4 1.5rem 24px
5 2rem 32px
6 2.5rem 40px
7 3rem 48px
8 4rem 64px
9 5rem 80px
10 6rem 96px
11 7rem 112px
12 8rem 128px
SCSS
@include "glidecss/defaults/spacing";

.button {
  padding: theme(spacing, 3);
}
CSS
.button {
  padding: 1rem;
}

Font family

The default font families are accessed with theme(font, family, <name>).

Name Value
sans ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, “Noto Sans”, sans-serif,“Apple Color Emoji”, “Segoe UI Emoji”, “Segoe UI Symbol”,“Noto Color Emoji”
serif ui-serif, Georgia, Cambria, “Times New Roman”, Times, serif
mono ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, “Liberation Mono”, “Courier New”, monospace
SCSS
@include "glidecss/defaults/font/family";

.heading {
  font-family: theme(font, family, serif);
}
CSS
.heading {
  font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
}

Font size

The default font sizes are accessed with theme(font, size, <name>).

Name Size (default) Pixels (default) Size (lg) Pixels (lg)
1 0.75rem 12px
2 0.875rem 14px
3 1rem 16px
4 1.125rem 18px 1.25rem 20px
5 1.375rem 22px 1.5rem 24px
6 1.625rem 26px 2rem 32px
7 2rem 32px 2.5rem 40px
8 2.5rem 40px 3rem 48px
SCSS
@include "glidecss/defaults/font/size";

.heading {
  @include screens(font, size, 7) using ($font-size) {
    font-size: $font-size;
  }
}
CSS
.heading {
  font-size: 2rem;
}

@media (min-width: 1024px) {
  .heading {
    font-size: 2.5rem;
  }
}

Font leading

The default font line heights are accessed with theme(font, leading, <name>).

Name Leading
DEFAULT 1.5
tight 1.25
loose 1.75
SCSS
@include "glidecss/defaults/font/leading";

.heading {
  line-height: theme(font, leading, tight);
}
CSS
.heading {
  line-height: 1.75;
}

Font weight

The default font weights are accessed with theme(font, weight, <name>).

Name Weight
light 300
normal 400
medium 500
bold 700
SCSS
@include "glidecss/defaults/font/weight";

.heading {
  font-weight: theme(font, weight, bold);
}
CSS
.heading {
  font-weight: 700;
}

Overrides

Note that if you want to override any defaults, the overrides should be declared before including the defaults file. This is due to theme performing a non-destrutive merge; in a similar fashion to the !default operator in Sass.

SCSS
@include theme((
  spacing: (
    0: .125rem,
    1: .5rem
  )
));

@include "glidecss/defaults";

This will add an additional 0 spacing property, and define a larger than normal 1 property. When the defaults are included, they will leave both of these initial values alone, and only add the remaining properties.

The non-destrutive merge is a super powerful tool when overriding component properties, allowing for a base set of reusable components that can be customised on a project basis.

SCSS
@include theme((
  card: (
    color: palette(primary, 1),
    background: palette(accent)
  )
));

@include "card";

Installation

Yarn

yarn add glidecss --dev

NPM

npm install -D glidecss

Import

SCSS
@import "glidecss/base";
@import "glidecss/reset";     // <- optional
@import "glidecss/defaults";  // <- optional

Development

Tests

The test suite will automatically be run by GitHub Actions on a push or pull request.

Quality

To manually run the Jest test suite:

yarn test

Releases

Releases are automatically handled by GitHub Actions. Just set the semantic version number in package.json, tag, push and the rest will happen automatically.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/thelucid/glidecss. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The library is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Glide CSS project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.