Class-light CSS blades inspired by Pico.css

Table of Contents

Install

Install CSS blades via CDN or npm

Via CDN:

<link href="https://cdn.jsdelivr.net/npm/@anydigital/blades@^0.27.0-alpha/assets/blades.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/@anydigital/blades@^0.27.0-alpha/assets/blades.theme.min.css" rel="stylesheet" /><!-- optional -->

Via npm:

npm install @anydigital/blades

Then import in your .css:

@import "@anydigital/blades";
@import "@anydigital/blades.theme"; /* optional */

Living example: /anydigital/build-awesome-starter/blob/main/_styles/styles.css


Layout

Global styles:

html {
  /* Prevent horizontal overflow and scrolling, modern way. */
  overflow-x: clip;
}
body {
  /* Ensure `body` takes at least the full height of the viewport (using dynamic viewport height for better mobile support). */
  min-height: 100dvh;
}

🥷 Breakout elements

Framework-agnostic utilities for breaking out images and figures beyond their container width.

Use the .breakout class to allow elements to extend beyond their parent container.

Demo & Docs →

Auto-columns

.columns,
[data-is-toc] > ul,
[data-is-toc] > ol {
  columns: 30ch auto; /* 2 cols max for 65ch container */

  /* Avoid breaking inside elements, such as nested lists */
  > * {
    break-inside: avoid;
  }
}

Table of contents ([data-is-toc]) has auto-columns by default.

Jump to top

[data-jump-to="top"] {
  position: fixed;
  bottom: 0;
  right: 0;
  padding-top: 50vh;
  opacity: 25%;

  &:hover {
    opacity: 75%;
  }
}

Content

Global styles:

html {
  /* Enable font smoothing */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  /* Enable global hyphenation */
  hyphens: auto;
  /* ... except for links and tables which are better (safer) without hyphenation */
  a,
  table {
    hyphens: none;
  }
}

Use Blades' <i>-helper to wrap emojis, favicons, or simply drop Font Awesome icons inside links. It automatically handles sizing and alignment while preventing underline on icons.

Demo & Docs →

Heading anchors

Set data-is-anchor attribute on anchors inside headings to position them absolutely, and show only on hover (when supported):

Heading with anchor #

How it works:

h1,
h2,
h3,
h4,
h5,
h6 {
  position: relative;

  [data-is-anchor] {
    position: absolute;
    right: 100%;
    top: 50%;
    transform: translateY(-50%);
    padding-right: 0.2ch;
    color: silver;
    text-decoration: none;
    visibility: hidden;
  }
  /* Avoid double-tap on touch devices */
  @media (hover: hover) {
    &:hover {
      [data-is-anchor] {
        visibility: visible;
      }
    }
  }
}

PRO-TIP for 11ty + markdown-it-anchor: /anydigital/eleventy-blades/blob/main/src/eleventy.config.js

List markers

Use inline style="--list-marker:'🥷 '" on <ul>/<ol> or even individual <li> to customize list markers:

How it works:

ul,
ol {
  &[style*="--list-marker:"] {
    list-style-type: var(--list-marker);

    > li {
      list-style-type: inherit;
    }
  }

  li[style*="--list-marker:"] {
    list-style-type: var(--list-marker);
  }
  li[data-marker]::marker {
    /* ⚠️ `data-marker` works only in Chrome and Firefox */
    content: attr(data-marker) " ";
  }
}

Unlist

Helper class to remove list styling at all:

.unlist {
  padding-inline-start: 0;

  > li {
    list-style: none;
  }
}

Code

Extends /picocss/pico/blob/main/scss/content/_code.scss

pre {
  padding: 1rem 1.5rem;
  padding-inline-end: 2rem;

  @media screen and (max-width: 767px) {
    border-radius: 0;
  }
}

code {
  /* Code block caption via data-attr (to display filename, etc.) */
  &[data-caption] {
    &::before {
      content: attr(data-caption);
      display: block;
      margin-bottom: 1rem;
      opacity: 50%;
      font-style: italic;
    }
  }

  &:where(pre > *) {
    padding: 0;
  }
}

/*** Extends https://github.com/PrismJS/prism/blob/master/plugins/treeview/prism-treeview.css ***/
.token.treeview-part {
  .entry-line {
    width: 2.5em !important;
    opacity: 25%;
  }
  .entry-name:last-child {
    opacity: 50%;

    &::before {
      display: none !important;
    }
  }
}

Table

🥷 Responsive table without wrapper

<table class="responsive"> allows a table to full-bleed and scroll on mobile.

Demo & Docs →

Horizontal expanders

Simply insert <hr> inside <th> to forcibly widen too narrow columns (especially useful in markdown):

<th>Col <hr></th>

Example table before:

Wide tableswith manycolumns mightget collapsedand be reallyhard to read!

Same table after adding <hr>-expanders:

Wide tables
with many
columns might
get collapsed
and be really
hard to read!

Living examples of big tables with <hr>-expanders:

How it works:

table {
  th {
    hr {
      width: 12ch; /* min ~65/12 = ~5 cols */
      height: 0;
      margin: 0;
      visibility: hidden;

      &.lg {
        width: 18ch;
      }
      &.x2 {
        width: 24ch;
      }
    }
  }
}

Borderless table

<table class="borderless"> removes all default borders:

Less borders
more fun

Living example: /#minimal-dependencies table


Utilities

Auto-dark

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    .dark-auto {
      filter: invert(100%) hue-rotate(180deg);
    }
  }
}

Faded

.faded {
  opacity: 50%;
  &:hover {
    opacity: 87.5%;
  }
}

Invert

/* Extends https://tailwindcss.com/docs/filter-invert */
.invert {
  /* Fix the scrollbar color when inverted */
  ::-webkit-scrollbar {
    filter: invert(1) !important;
  }
}

Unreduce motion

@utility unreduce-animation-* {
  @media (prefers-reduced-motion: reduce) {
    &:not([aria-busy="true"]) {
      animation-duration: --value([ *]) !important;
      animation-delay: 0 !important;
      animation-iteration-count: infinite !important;
    }
  }
}

Theme (optional)


How it works
@import "./float-label.theme";

body {
  /* Make the `body` a flex container with column layout, and `main` to automatically fill available space. This is useful for creating sticky footers and full-height layouts. */
  display: flex;
  flex-direction: column;
  > main {
    flex-grow: 1;
  }
}

a {
  &:not([href^="#"]) {
    text-decoration-thickness: 1px;
    &:hover {
      text-decoration-thickness: 2px;
    }
  }
}

h1 {
  font-size: 2.5em; /* for pico.css & tw-typography */
  margin-bottom: 1rem; /* for tw-typography */
}

/* Potential fix https://github.com/picocss/pico/blob/main/css/pico.css for the very first headings
:where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) ~ :is(h1, h2, h3, h4, h5, h6) {
  margin-top: var(--pico-typography-spacing-top);
}
h1,
h2,
h3,
h4,
h5,
h6 {
  & ~ & {
    margin-bottom: 2rem;
  }
}
NOTE: be careful with wrapped headings, i.e. inside nav: https://blades.ninja/build-awesome-11ty/#usage
*/

hr {
  margin-block: 2em; /* for pico.css & tw-typography */
}

ul {
  ul {
    font-size: 87.5%;
  }
}

pre {
  small {
    opacity: 75%;
    font-weight: lighter;
  }
}

table {
  th {
    vertical-align: bottom;
    font-weight: bold;
  }
  td {
    vertical-align: top;
  }
  pre {
    margin-bottom: 0.25rem;
  }
}

[data-jump-to="top"] {
  > i {
    display: inline-block;
    padding: 0.25rem 0.375rem;
    margin: 0.5rem;
    font-size: 0.75rem;
    color: black;
    border-color: black;
  }
}

[data-is-toc] {
  font-size: 87.5%;

  a {
    text-decoration: none;
  }
  > ul > * > a {
    font-weight: 500;
  }
}

.breakout,
.breakout-all {
  > img,
  > figure {
    margin-bottom: 1rem;
  }
}

.faded {
  a {
    text-decoration-style: dotted;
  }
}

See also: