@chriscalo/css-utils

A collection of common CSS utilities for rapid prototyping.

Usage no npm install needed!

<script type="module">
  import chriscaloCssUtils from 'https://cdn.skypack.dev/@chriscalo/css-utils';
</script>

README

css-utils

A collection of common CSS utilities for rapid prototyping.

When you're trying to move quickly, typing out class="" is far too slow. This library explores the idea of applying common styles via attributes.

Get started

The easiest way to get started is by linking to the latest from any web page via unpkg.com:

<!-- will redirect to the latest version -->
<link
  href="https://unpkg.com/@chriscalo/css-utils/dist/css-utils.css"
  rel="stylesheet"
/>

Linking to the latest version may break things when the library changes, so to link to a specific version:

  1. Go to unpkg.com/@chriscalo/css-utils/dist/css-utils.css
  2. You'll be redirected to a URL with the latest version embedded in it
  3. Copy that URL and use it in your <link> tag

For example:

<!-- notice the version is specified -->
<link
  href="https://unpkg.com/@chriscalo/css-utils@0.18.0/dist/css-utils.css"
  rel="stylesheet"
/>

But you can also install these styles into your project via npm / yarn:

npm install @chriscalo/css-utils
# or
yarn add @chriscalo/css-utils

If your build tool knows to check the node_modules folder, you can then reference the stylesheet from your HTML:

<link
  rel="stylesheet"
  href="@chriscalo/css-utils/dist/css-utils.css"
/>

Otherwise, you'll need to tweak the href path.

Usage

Here are usage examples to explain the API.

Layout debugging

Confused why a layout is doing what it's doing? Make the bounding boxes visible:

<!-- outline: solid 1px magenta -->
<div outline>
  <div outline>child</div>
  <div outline>child</div>
  <div outline>child</div>
</div>

Layout settings

Apply common layouts:

<div hidden></div>
<div block></div>
<div inline-block></div>
<div grid></div>
<div relative></div> or <div container></div>
<div absolute></div> or <div positioned></div>
<div fixed></div>

Nudging

Nudge in-flow elements:

<span nudge-down-1></span>   <!-- down 1px -->
<span nudge-up-2></span>     <!-- up 2px -->
<span nudge-right-3></span>  <!-- right 3px -->
<span nudge-left-4></span>   <!-- left 4px -->

The full list of nudge values are:

nudge-down-1  nudge-up-1  nudge-left-1  nudge-right-1
nudge-down-2  nudge-up-2  nudge-left-2  nudge-right-2
nudge-down-3  nudge-up-3  nudge-left-3  nudge-right-3
nudge-down-4  nudge-up-4  nudge-left-4  nudge-right-4
nudge-down-6  nudge-up-6  nudge-left-6  nudge-right-6
nudge-down-8  nudge-up-8  nudge-left-8  nudge-right-8

Edge pinning for positioned layers

There are helpers for edge-pinning absolutely-positioned layers:

<div absolute pin-all>
  <!-- will fill nearest containing block -->
</div>

<div fixed pin-all>
  <!-- will fill the viewport -->
</div>

<div absolute pin-all unpin-left width-32>
  <!-- will create a left-docked panel -->
</div>

Here are of the available pinning values:

pin-all
pin-top
pin-right
pin-bottom
pin-left
pin-x
pin-y

unpin-all
unpin-top
unpin-right
unpin-bottom
unpin-left
unpin-x
unpin-y

Flexbox layout

Simplify flexbox layout:

<!-- flex-row or stack-x or stack-h -->
<div flex-row>
  <span grow shrink>Item</span>
  <span spacer></span> <!-- [spacer], [grow] { flex-grow: 1 } -->
  <span no-shrink>Item</span>
</div>

<!-- flex-col or stack-y or stack-v -->
<div flex-col>
  <span shrink no-grow>Item</span>
  <span spacer></span> <!-- [spacer], [grow] { flex-grow: 1 } -->
  <span grow shrink>Item</span>
</div>

Synonyms for flex-row are stack-h and stack-x. Similarly, synonyms for flex-col are stack-v and stack-y.

It's possible to set gaps between flex items:

<div flex-row gap-1>
  <span>Item</span>
  <!-- 8px gap -->
  <span>Item</span>
  <!-- 8px gap -->
  <span>Item</span>
</div>

<div stack-y gap-0.5>
  <span>Item</span>
  <!-- 4px gap -->
  <span>Item</span>
  <!-- 4px gap -->
  <span>Item</span>
</div>

All of the possible values for flex gaps are:

gap-0
gap-0.5
gap-1
gap-1.5
gap-2
gap-2.5
gap-3
gap-3.5
gap-4
gap-4.5
gap-5
gap-6
gap-7
gap-8
gap-9
gap-10
gap-11
gap-12
gap-13
gap-14
gap-16
gap-18
gap-20
gap-24
gap-28
gap-32

Main-axis alignment (2nd and 3rd columns are aliases):

main:start       items:main-start       items:line-start
main:center      items:main-center      items:line-center
main:end         items:main-end         items:line-end
main:distribute  items:main-distribute  items:line-distribute
main:even        items:main-even        items:line-even

Cross-axis alignment (2nd column is aliases):

cross:start     items:cross-start
cross:center    items:cross-center
cross:end       items:cross-end
cross:baseline  items:cross-baseline
cross:fill      items:cross-fill

Flex item overflow along main-axis:

items:fit
items:wrap          items:wrap-after
items:wrap-reverse  items:wrap-before
items:overflow

Flex line alignment:

lines:start
lines:center
lines:end
lines:distribute
lines:even
lines:fill

Helpers for flex item cross-axis alignment:

<div stack-v gap-1>
  <span self:cross-start>item</span>     <!-- align-self: flex-start -->
  <span self:cross-center>item</span>    <!-- align-self: center -->
  <span self:cross-end>item</span>       <!-- align-self: flex-end -->
  <span self:cross-baseline>item</span>  <!-- align-self: baseline -->
  <span self:cross-fill>item</span>      <!-- align-self: stretch -->
</div>

Grid layout

Helpers for grid layout:

<div grid gap-1>
  <span>One</span>
   <!-- 8px gap here -->
  <span>Two</span>
</div>

All of the possible values for grid gaps are:

gap-0    gap-x-0    gap-y-0
gap-0.5  gap-x-0.5  gap-y-0.5
gap-1    gap-x-1    gap-y-1
gap-1.5  gap-x-1.5  gap-y-1.5
gap-2    gap-x-2    gap-y-2
gap-2.5  gap-x-2.5  gap-y-2.5
gap-3    gap-x-3    gap-y-3
gap-3.5  gap-x-3.5  gap-y-3.5
gap-4    gap-x-4    gap-y-4
gap-4.5  gap-x-4.5  gap-y-4.5
gap-5    gap-x-5    gap-y-5
gap-6    gap-x-6    gap-y-6
gap-7    gap-x-7    gap-y-7
gap-8    gap-x-8    gap-y-8
gap-9    gap-x-9    gap-y-9
gap-10   gap-x-10   gap-y-10
gap-11   gap-x-11   gap-y-11
gap-12   gap-x-12   gap-y-12
gap-13   gap-x-13   gap-y-13
gap-14   gap-x-14   gap-y-14
gap-16   gap-x-16   gap-y-16
gap-18   gap-x-18   gap-y-18
gap-20   gap-x-20   gap-y-20
gap-24   gap-x-24   gap-y-24
gap-28   gap-x-28   gap-y-28
gap-32   gap-x-32   gap-y-32

Force a grid item to fill the grid:

<div grid>
  <!-- will fill the entire grid -->
  <span fill-grid>Fill grid</span>
</div>

Layout utilities

To center content:

 <!-- Set an explicit height if it needs to be larger than its content -->
<div center-xy> <!-- or `content:center-xy` -->
  <div>I'm centered!</div>
</div>

Spacing and sizing

Apply padding and margin based on an 8px grid:

<div pad-0></div>   <!-- padding: 0; -->
<div pad-0.5></div> <!-- padding: 4px; -->
<div pad-1></div>   <!-- padding: 8px; -->
<div pad-2></div>   <!-- padding: 16px; -->
<div pad-10></div>  <!-- padding: 80px; -->

<div margin-0></div>   <!-- margin: 0 -->
<div margin-0.5></div> <!-- margin: 4px -->
<div margin-1></div>   <!-- margin: 8px -->
<div margin-2></div>   <!-- margin: 16px -->
<div margin-10></div>  <!-- margin: 80px -->

But it's also possible to apply padding and margin only to specific sides:

<div pad-x-0></div>      <!-- padding-left: 0; padding-right: 0; -->
<div pad-y-0.5></div>    <!-- padding-top: 4px; padding-bottom: 4px; -->
<div pad-top-1></div>    <!-- padding-top: 8px; -->
<div pad-t-1></div>      <!-- padding-top: 8px; -->
<div pad-right-2></div>  <!-- padding-right: 16px; -->
<div pad-r-2></div>      <!-- padding-right: 16px; -->
<div pad-bottom-4></div> <!-- padding-bottom: 32px; -->
<div pad-b-4></div>      <!-- padding-bottom: 32px; -->
<div pad-left-8></div>   <!-- padding-left: 64px; -->
<div pad-l-8></div>      <!-- padding-left: 64px; -->

<div margin-x-0></div>      <!-- margin-left: 0; margin-right: 0; -->
<div margin-y-0.5></div>    <!-- margin-top: 4px; margin-bottom: 4px; -->
<div margin-top-1></div>    <!-- margin-top: 8px; -->
<div margin-t-1></div>      <!-- margin-top: 8px; -->
<div margin-right-2></div>  <!-- margin-right: 16px; -->
<div margin-r-2></div>      <!-- margin-right: 16px; -->
<div margin-bottom-4></div> <!-- margin-bottom: 32px; -->
<div margin-b-4></div>      <!-- margin-bottom: 32px; -->
<div margin-left-8></div>   <!-- margin-left: 64px; -->
<div margin-l-8></div>      <!-- margin-left: 64px; -->

Use the same system for height, width, or both via size:

<div height-0.5></div> <!-- height: 4px; -->
<div width-10></div>   <!-- width: 80px; -->
<div size-18></div>    <!-- width: 144px; height: 144px; -->

It's also possible to specify that elements should fill their available width, height, or both:

<div height-full></div> <!-- height: 100%; -->
<div width-full></div>  <!-- width: 100%; -->
<div size-full></div>   <!-- width: 100%; height: 100%; -->

The full scale of spacing and sizing values are:

 0    =>  0
 0.5  =>  4px
 1    =>  8px
 1.5  =>  12px
 2    =>  16px
 2.5  =>  20px
 3    =>  24px
 3.5  =>  28px
 4    =>  32px
 4.5  =>  36px
 5    =>  40px
 6    =>  48px
 7    =>  56px
 8    =>  64px
 9    =>  72px
10    =>  80px
11    =>  88px
12    =>  96px
13    =>  104px
14    =>  112px
16    =>  128px
18    =>  144px
20    =>  160px
24    =>  192px
28    =>  224px
32    =>  256px

Opacity

Apply opacities:

<div opacity-none></div> <!-- opacity: 0 -->
<div opacity-0></div>    <!-- opacity: 0 -->
<div opacity-10></div>   <!-- opacity: 0.10 -->
<div opacity-20></div>   <!-- opacity: 0.20 -->
<div opacity-25></div>   <!-- opacity: 0.25 -->
<div opacity-30></div>   <!-- opacity: 0.30 -->
<div opacity-40></div>   <!-- opacity: 0.40 -->
<div opacity-50></div>   <!-- opacity: 0.50 -->
<div opacity-60></div>   <!-- opacity: 0.60 -->
<div opacity-70></div>   <!-- opacity: 0.70 -->
<div opacity-75></div>   <!-- opacity: 0.75 -->
<div opacity-80></div>   <!-- opacity: 0.80 -->
<div opacity-90></div>   <!-- opacity: 0.90 -->
<div opacity-100></div>  <!-- opacity: 1 -->
<div opacity-full></div> <!-- opacity: 1 -->

Icons

Resize icon fonts:

<i icon-xs class="material-icons icon">add</i>    <!-- 16px -->
<i icon-s class="material-icons icon">close</i>   <!-- 20px -->
<i icon-m class="material-icons icon">brush</i>   <!-- 24px -->
<i icon-l class="material-icons icon">notes</i>   <!-- 28px -->
<i icon-xl class="material-icons icon">layers</i> <!-- 32px -->

Typography

Resize text:

<p text-xxxs></p> <!-- font-size: 11px -->
<p text-xxs></p>  <!-- font-size: 12px -->
<p text-xs></p>   <!-- font-size: 13px -->
<p text-s></p>    <!-- font-size: 14px -->
<p text-m></p>    <!-- font-size: 16px -->
<p text-l></p>    <!-- font-size: 20px -->
<p text-xl></p>   <!-- font-size: 24px -->
<p text-xxl></p>  <!-- font-size: 32px -->
<p text-xxxl></p> <!-- font-size: 48px -->

Set text weights:

<p text-weight-hairline></p>  <!-- font-weight: 100; -->
<p text-weight-thin></p>      <!-- font-weight: 200; -->
<p text-weight-light></p>     <!-- font-weight: 300; -->
<p text-weight-normal></p>    <!-- font-weight: 400; -->
<p text-weight-medium></p>    <!-- font-weight: 500; -->
<p text-weight-semibold></p>  <!-- font-weight: 600; -->
<p text-weight-bold></p>      <!-- font-weight: 700; -->
<p text-weight-extrabold></p> <!-- font-weight: 800; -->
<p text-weight-black></p>     <!-- font-weight: 900; -->

Apply text transforms:

<span capitalize></span>
<span uppercase></span>
<span lowercase></span>

Prevent long words from overflowing their containers:

<span break-word></span>

Apply horizontal and vertical alignment to text:

<span align-left></span>         <!-- or text-align-left -->
<span align-center></span>       <!-- or text-align-center -->
<span align-right></span>        <!-- or text-align-right -->
<span align-justify></span>      <!-- or text-align-justify -->

<span align-baseline></span>     <!-- or text-align-baseline -->
<span align-sub></span>          <!-- or text-align-sub -->
<span align-super></span>        <!-- or text-align-super -->
<span align-text-top></span>     <!-- or text-align-text-top -->
<span align-text-bottom></span>  <!-- or text-align-text-bottom -->
<span align-middle></span>       <!-- or text-align-middle -->
<span align-top></span>          <!-- or text-align-top -->
<span align-bottom></span>       <!-- or text-align-bottom -->

Apply greyscale text colors and modify them on hover:

<div color-000></div>
<div color-111></div>
<div color-222></div>
<div color-333></div>
<div color-444></div>
<div color-555></div>
<div color-666></div>
<div color-777></div>
<div color-888></div>
<div color-999></div>
<div color-aaa></div>
<div color-bbb></div>
<div color-ccc></div>
<div color-ddd></div>
<div color-eee></div>
<div color-fff></div>

<div hover-color-000></div>
<div hover-color-111></div>
<div hover-color-222></div>
<div hover-color-333></div>
<div hover-color-444></div>
<div hover-color-555></div>
<div hover-color-666></div>
<div hover-color-777></div>
<div hover-color-888></div>
<div hover-color-999></div>
<div hover-color-aaa></div>
<div hover-color-bbb></div>
<div hover-color-ccc></div>
<div hover-color-ddd></div>
<div hover-color-eee></div>
<div hover-color-fff></div>

Background colors

Do the same for background colors:

<div bg-000></div>
<div bg-111></div>
<div bg-222></div>
<div bg-333></div>
<div bg-444></div>
<div bg-555></div>
<div bg-666></div>
<div bg-777></div>
<div bg-888></div>
<div bg-999></div>
<div bg-aaa></div>
<div bg-bbb></div>
<div bg-ccc></div>
<div bg-ddd></div>
<div bg-eee></div>
<div bg-fff></div>

<div hover-bg-000></div>
<div hover-bg-111></div>
<div hover-bg-222></div>
<div hover-bg-333></div>
<div hover-bg-444></div>
<div hover-bg-555></div>
<div hover-bg-666></div>
<div hover-bg-777></div>
<div hover-bg-888></div>
<div hover-bg-999></div>
<div hover-bg-aaa></div>
<div hover-bg-bbb></div>
<div hover-bg-ccc></div>
<div hover-bg-ddd></div>
<div hover-bg-eee></div>
<div hover-bg-fff></div>

Borders

Apply borders:

<div stroke></div>
<div stroke-y></div> <!-- top and bottom -->
<div stroke-x></div> <!-- left and right -->
<div stroke-top></div>
<div stroke-right></div>
<div stroke-bottom></div>
<div stroke-left></div>

Apply rounded corners:

<div rounded-none></div> <!-- 0 -->
<div rounded-xs></div>   <!-- 1px -->
<div rounded-s></div>    <!-- 2px -->
<div rounded-m></div>    <!-- 4px -->
<div rounded-l></div>    <!-- 8px -->
<div rounded-xl></div>   <!-- 16px -->
<div rounded-xxl></div>  <!-- 32px -->

It's also possible to selectively apply corner rounding:

<div rounded-top-none></div> <!-- 0 -->
<div rounded-top-xs></div>   <!-- 1px -->
<div rounded-top-s></div>    <!-- 2px -->
<div rounded-top-m></div>    <!-- 4px -->
<div rounded-top-l></div>    <!-- 8px -->
<div rounded-top-xl></div>   <!-- 16px -->
<div rounded-top-xxl></div>  <!-- 32px -->

<div rounded-left-none></div> <!-- 0 -->
<div rounded-left-xs></div>   <!-- 1px -->
<div rounded-left-s></div>    <!-- 2px -->
<div rounded-left-m></div>    <!-- 4px -->
<div rounded-left-l></div>    <!-- 8px -->
<div rounded-left-xl></div>   <!-- 16px -->
<div rounded-left-xxl></div>  <!-- 32px -->

<div rounded-right-none></div> <!-- 0 -->
<div rounded-right-xs></div>   <!-- 1px -->
<div rounded-right-s></div>    <!-- 2px -->
<div rounded-right-m></div>    <!-- 4px -->
<div rounded-right-l></div>    <!-- 8px -->
<div rounded-right-xl></div>   <!-- 16px -->
<div rounded-right-xxl></div>  <!-- 32px -->

<div rounded-bottom-none></div> <!-- 0 -->
<div rounded-bottom-xs></div>   <!-- 1px -->
<div rounded-bottom-s></div>    <!-- 2px -->
<div rounded-bottom-m></div>    <!-- 4px -->
<div rounded-bottom-l></div>    <!-- 8px -->
<div rounded-bottom-xl></div>   <!-- 16px -->
<div rounded-bottom-xxl></div>  <!-- 32px -->

There's even a shortcut for applying rounding when the element is the first or last child with the following attributes. This is helpful for creating pill button-like styling when it's inconvenient to selectively apply attributes to the first and last children.

<!-- horizontal pill -->
<button rounded-x-first-last-m>First</button>  <!-- rounded-left-m -->
<button rounded-x-first-last-m>Middle</button> <!-- no rounding applied -->
<button rounded-x-first-last-m>Last</button>   <!-- rounded-right-m -->

<!-- vertical pill -->
<button rounded-y-first-last-m>First</button>  <!-- rounded-top-m -->
<button rounded-y-first-last-m>Middle</button> <!-- no rounding applied -->
<button rounded-y-first-last-m>Last</button>   <!-- rounded-bottom-m -->

Interaction

There are some attributes to apply styling that affects interaction:

<div no-select>This text not selectable</div>
<div no-mouse>Anything inside this element will receive no mouse events</div>
<div scroll-x>scrollable in the x direction</div>
<div scroll-y>scrollable in the y direction</div>

Apply cursor values:

<div cursor-auto></div>
<div cursor-default></div>
<div cursor-none></div>
<div cursor-context-menu></div>
<div cursor-help></div>
<div cursor-pointer></div>
<div cursor-progress></div>
<div cursor-wait></div>
<div cursor-cell></div>
<div cursor-crosshair></div>
<div cursor-text></div>
<div cursor-vertical-text></div>
<div cursor-alias></div>
<div cursor-copy></div>
<div cursor-move></div>
<div cursor-no-drop></div>
<div cursor-not-allowed></div>
<div cursor-e-resize></div>
<div cursor-n-resize></div>
<div cursor-ne-resize></div>
<div cursor-nw-resize></div>
<div cursor-s-resize></div>
<div cursor-se-resize></div>
<div cursor-sw-resize></div>
<div cursor-w-resize></div>
<div cursor-ew-resize></div>
<div cursor-ns-resize></div>
<div cursor-nesw-resize></div>
<div cursor-nwse-resize></div>
<div cursor-col-resize></div>
<div cursor-row-resize></div>
<div cursor-all-scroll></div>
<div cursor-zoom-in></div>
<div cursor-zoom-out></div>
<div cursor-grab></div>
<div cursor-grabbing></div>

Local development

To develop, run the following commands:

yarn # install dependencies
yarn run watch # start dev mode

Publish to npm

First, update the version:

npm version patch
# or
npm version minor
# or
npm version major

Then log in and publish:

npm login
npm publish

Production use

Not recommended. There are no tests, and there's no guarantee that these attribute names don't conflict with other libraries.

Inspiration

Heavily inspired by the utility-first CSS framework Tailwind.