# Lesson (Component, Card)

> A card for a video lesson: a 16:9 thumbnail with the duration, the module, the title and whether it's a free preview or part of the paid course.

- Type: component
- Preview: https://sprucecss.com/ui/card/lesson/
- Markdown: https://sprucecss.com/ui/card/lesson.md
- Framework: Spruce CSS v3 (`npm install sprucecss`). Every SCSS file below starts with `@use 'sprucecss/scss/spruce' as *;`, so compile it with `node_modules` as a Sass load path (`sass --load-path=node_modules`).

## How to use it

1. Copy the SCSS of this component and of every stylesheet dependency into your project, in the order listed under Dependencies, and compile them with your Spruce CSS setup.
2. Copy the HTML.
3. Replace the demo content (text, links, image URLs) in the HTML; keep the classes, the structure and the accessibility attributes.

The HTML below is rendered with the default content. In the Spruce UI source (Nunjucks) the content comes from a data object: Override with `lessons` ([{ title, module, duration, image, free }]).

## Dependencies

Stylesheets, in this order (each one is `{name}.scss` in the library; its endpoint has the code):

1. `card/lesson` (this component, below)

## HTML

```html
<ul class="lesson-cards" role="list">
    <li>
        <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">The 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>
        <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="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="#">Container queries in practice</a></h3>
                <p class="lesson-card__access">Included in the video course</p>
            </div>
        </article>
    </li>
    <li>
        <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>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">Design 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>
</ul>
```

## SCSS

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

.lesson-cards {
  @include clear-list;
  @include layout-grid('m', 18rem);

  // The items sit side by side: clear the stack margins of the base list style.
  > li {
    margin-block: 0;
    padding-block: 0;
  }
}

.lesson-card {
  @include a11y-card-link('.lesson-card__link', true);
  background-color: color('surface');
  block-size: 100%;
  border: config('border-width-base', $display) solid color('border');
  border-radius: radius('lg');
  box-shadow: shadow('sm');
  display: flex;
  flex-direction: column;
  overflow: hidden;

  &__media {
    aspect-ratio: 16 / 9;
    background-color: color('surface-muted');
    overflow: hidden;
    position: relative;

    img {
      block-size: 100%;
      inline-size: 100%;
      object-fit: cover;
    }

    // A shade, so the icon and the duration stay readable on any image.
    &::after {
      background-image: linear-gradient(to top, hsl(0deg 0% 0% / 45%), transparent 60%);
      content: '';
      inset: 0;
      position: absolute;
    }
  }

  &__duration {
    background-color: hsl(0deg 0% 0% / 70%);
    border-radius: radius('sm');
    color: hsl(0deg 0% 100%);
    font-size: config('font-size-sm', $typography);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    inset-block-end: spacer('xs');
    inset-inline-end: spacer('xs');
    line-height: 1.4;
    padding: 0 spacer('xxs');
    position: absolute;
  }

  &__icon {
    align-items: center;
    background-color: color('surface');
    block-size: 2.75rem;
    border-radius: 50%;
    box-shadow: shadow('md');
    color: color('primary');
    display: flex;
    inline-size: 2.75rem;
    inset: 0;
    justify-content: center;
    margin: auto;
    position: absolute;

    svg {
      block-size: 1.15rem;
      inline-size: 1.15rem;
    }
  }

  &__body {
    flex-grow: 1;
    padding: spacer('s') spacer('m') spacer('m');
    @include layout-stack('xxs'); // module, title, access
  }

  &__module {
    color: color('primary');
    font-size: config('font-size-sm', $typography);
    font-weight: 700;
  }

  &__title {
    font-size: font-size('h5');
  }

  &__link {
    color: inherit;
    text-decoration: none;
  }

  &__access {
    color: color('muted');
    font-size: config('font-size-sm', $typography);

    &--free {
      color: color('success', 'alert');
      font-weight: 600;
    }
  }

  @include motion-safe {
    img {
      transition: scale 0.3s ease;
    }

    &:hover img {
      scale: 1.04;
    }
  }
}
```

## Notes

## Technical Details

- The whole card is clickable with the `a11y-card-link()` mixin, but only the title is a link, so screen readers hear the lesson title once.
- The play icon (free preview) and the lock icon (paid lesson) are decorative; the text under the title says the same ("Free preview", "Included in the video course"), so the meaning doesn't depend on the icon or the color.
- The duration has a hidden "Duration:" label and tabular numbers; a shade at the bottom of the image keeps it readable on any thumbnail.
- The cards sit in a `layout-grid()` list; override them with `lessons` (`[{ title, module, duration, image, free }]`).
