Video Library

The video course

Watch it built, line by line

64 short lessons in six modules. Start with the free previews.

lessons
64
of video
12 h
free previews
8

See all 64 lessons

@use 'sprucecss/scss/spruce' as *;

.video-library-block {
  padding-block: spacer-clamp('xl', 'xxl');

  &__inner {
    display: flex;
    flex-direction: column;
    gap: spacer('l');
  }

  &__stats {
    display: flex;
    flex-wrap: wrap;
    gap: spacer('s') spacer('xl');
    justify-content: center;
    margin-block: 0;
  }

  &__stat {
    align-items: baseline;
    display: flex;
    flex-direction: row-reverse;
    gap: spacer('xs');

    dt {
      color: color('muted');
      font-weight: 400;
    }

    dd {
      color: color('heading');
      font-family: config('font-family-heading', $typography);
      font-size: font-size('h3');
      font-weight: config('font-weight-heading', $typography);
      margin: 0;
    }
  }

  &__filters {
    display: flex;
    flex-wrap: wrap;
    gap: spacer('xs');
    justify-content: center;
  }

  &__more {
    margin-block: 0;
    text-align: center;
  }
}
<section class="video-library-block" id="video-library" aria-labelledby="video-library-title" data-video-library>
    <div class="container">
        <div class="video-library-block__inner">
            <header class="block-heading">
                <p class="block-heading__eyebrow">The video course</p>
                <h2 class="block-heading__title" id="video-library-title">Watch it built, line by line</h2>
                <p class="block-heading__text">64 short lessons in six modules. Start with the free previews.</p>
            </header>
            <dl class="video-library-block__stats">
                <div class="video-library-block__stat">
                    <dt>lessons</dt>
                    <dd>64</dd>
                </div>
                <div class="video-library-block__stat">
                    <dt>of video</dt>
                    <dd>12 h</dd>
                </div>
                <div class="video-library-block__stat">
                    <dt>free previews</dt>
                    <dd>8</dd>
                </div>
            </dl>
            <div class="video-library-block__filters" role="group" aria-label="Filter by module">
                <button class="btn btn--sm btn--pill btn--primary" type="button" data-filter="all" aria-pressed="true">All</button>
                <button class="btn btn--sm btn--pill btn--soft-primary" type="button" data-filter="cascade" aria-pressed="false">Cascade</button>
                <button class="btn btn--sm btn--pill btn--soft-primary" type="button" data-filter="layout" aria-pressed="false">Layout</button>
                <button class="btn btn--sm btn--pill btn--soft-primary" type="button" data-filter="tokens" aria-pressed="false">Tokens</button>
            </div>
            <ul class="lesson-cards" role="list">
                <li data-module="cascade">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>8:02</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M7 4v16l13-8z"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Cascade</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">How the cascade really works</a></h3>
                            <p class="lesson-card__access lesson-card__access--free">Free preview</p>
                        </div>
                    </article>
                </li>
                <li data-module="cascade">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1461749280684-dccba630e2f6?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>12:45</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Cascade</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">Cascade layers in a real project</a></h3>
                            <p class="lesson-card__access">Included in the video course</p>
                        </div>
                    </article>
                </li>
                <li data-module="layout">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>14:36</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M7 4v16l13-8z"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Layout</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">Container queries in practice</a></h3>
                            <p class="lesson-card__access lesson-card__access--free">Free preview</p>
                        </div>
                    </article>
                </li>
                <li data-module="layout">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>10:12</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Layout</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">A grid without breakpoints</a></h3>
                            <p class="lesson-card__access">Included in the video course</p>
                        </div>
                    </article>
                </li>
                <li data-module="tokens">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1587620962725-abab7fe55159?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>11:20</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Tokens</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">A token system that scales</a></h3>
                            <p class="lesson-card__access">Included in the video course</p>
                        </div>
                    </article>
                </li>
                <li data-module="tokens">
                    <article class="lesson-card">
                        <div class="lesson-card__media">
                            <img src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?auto=format&amp;fit=crop&amp;w=640&amp;q=70" alt="" loading="lazy" width="640" height="360">
                            <span class="lesson-card__duration"><span class="sr-only">Duration: </span>16:05</span>
                            <span class="lesson-card__icon" aria-hidden="true">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
                            </span>
                        </div>
                        <div class="lesson-card__body">
                            <p class="lesson-card__module">Tokens</p>
                            <h3 class="lesson-card__title"><a class="lesson-card__link" href="#">Dark mode in one afternoon</a></h3>
                            <p class="lesson-card__access">Included in the video course</p>
                        </div>
                    </article>
                </li>
            </ul>
            <p class="sr-only" aria-live="polite" data-video-library-status></p>
            <p class="video-library-block__more"><a class="btn btn--outline-primary" href="#">See all 64 lessons</a></p>
        </div>
    </div>
</section>
(() => {
  document.querySelectorAll('[data-video-library]').forEach((block) => {
    const buttons = [...block.querySelectorAll('[data-filter]')];
    const items = [...block.querySelectorAll('[data-module]')];
    const status = block.querySelector('[data-video-library-status]');

    buttons.forEach((button) => {
      button.addEventListener('click', () => {
        const filter = button.dataset.filter;
        let visible = 0;

        buttons.forEach((other) => {
          const isActive = other === button;

          other.setAttribute('aria-pressed', String(isActive));
          other.classList.toggle('btn--primary', isActive);
          other.classList.toggle('btn--soft-primary', !isActive);
        });

        items.forEach((item) => {
          const isMatch = filter === 'all' || item.dataset.module === filter;

          item.hidden = !isMatch;
          visible += isMatch ? 1 : 0;
        });

        status.textContent = `${visible} ${visible === 1 ? 'lesson' : 'lessons'} shown.`;
      });
    });
  });
})();

Video Library

On this page

A video course at a glance: the key numbers, a module filter and a grid of lesson cards, with the free previews marked.

Technical Details

  • It combines the Lesson Cards; load their stylesheet too.
  • The module filter is a group of toggle buttons (aria-pressed), and a polite live region announces how many lessons are shown. Without JavaScript every lesson stays visible.
  • The numbers are a description list shown value first (row-reverse), so they're read in a natural order ("lessons: 64").
  • The block has id="video-library", so the hero and the offer can link to it.
  • Override the content with videoLibraryBlock; every lesson has a module value (for the filter) and a moduleLabel (for the card).

Documentation

Learn about Spruce CSS through our extensive documentation.

Components

Explore our UI library of accessible components built with Spruce CSS.

Blocks and Pages

Full-width page blocks and complete page templates, ready to copy.

Find us on GitHub

Did you find a bug? Have an idea or a question? Please open an issue to help us develop the project.