Theme
On this page (3)
Component Skill — components/brand-logos/component-skill.md

Native basis

A flex-wrap row of inline SVGs. currentColor makes every mark follow --foreground - black in light mode, white in dark (the tokens flip) - so logos read at full theme ink; the row is softened with opacity instead of a muted color, and the name text stays --muted-foreground.

Web Platform APIs

Inline `<svg>currentColorflex-wrap

Classes

.mk-logos.mk-logos-row.mk-logos-roll.mk-logos-roll-track.mk-logo.mk-logo-name.mk-logos-caption

§Default

Four real marks (simple-icons) wrapped in links; wraps freely in narrow containers.

§Rolling

Continuous speed-based roll: the track holds the set twice (second copy aria-hidden) and loops seamlessly at --mk-logos-roll-duration (default 30s - smaller is faster). data-direction='y' rolls vertically inside a --mk-logos-roll-height viewport (default 12rem). Hover or keyboard focus pauses.

§CSS view file

/* -- Brand Logos (marketing) ---------------------------------------
   "Trusted by" row: inline SVG marks inherit currentColor so logos
   recolor with --muted-foreground under any theme. Wraps freely. */
@layer components {
  .mk-logos {
    container: mk-logos / inline-size;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    width: 100%;
    max-width: 80rem;
    margin-inline: auto;
    padding-inline: 1.25rem;
  }
  .mk-logos-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    column-gap: 2.5rem;
    row-gap: 1.75rem;
  }
  /* the mark links out to the brand - no underline. Ink color tracks the
     theme (black in light, white in dark) via --foreground; the name text
     stays muted so the marks read first. */
  .mk-logo {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
    color: var(--foreground);
    text-decoration: none;
    border-radius: var(--radius-md);
    opacity: 0.75;
    transition: opacity 150ms ease;
    &:hover { opacity: 1; }
    &:focus-visible { outline: 2px solid var(--ring); outline-offset: 4px; }
    & svg { width: 1.875rem; height: 1.875rem; aspect-ratio: 1; flex-shrink: 0; fill: currentColor; }
    & .mk-logo-name { color: var(--muted-foreground); }
  }
  .mk-logo-name {
    font-size: 1.125rem;
    font-weight: 500;
    letter-spacing: -0.025em;
    white-space: nowrap;
  }
  .mk-logos-caption {
    margin: 0;
    text-align: center;
    color: var(--muted-foreground);
    font-size: 0.875rem;
  }
  @container mk-logos (min-width: 30rem) {
    .mk-logo svg { width: 2.5rem; height: 2.5rem; }
    .mk-logo-name { font-size: 1.25rem; }
  }
  /* -- Rolling marquee ------------------------------------------
     Continuous speed-based roll (a slow ticker, not step sliding):
     the track holds the logo set TWICE - the second copy carries
     aria-hidden="true" + tabindex="-1" - and translates by exactly
     -50%, so the loop restarts on identical content (seamless).
     `gap` between items plus an equal padding at the track's end
     keeps each copy exactly half the border box wide (transform
     percentages measure the border box, padding included). */
  .mk-logos-roll {
    --mk-logos-roll-duration: 30s;
    --mk-logos-roll-height: 12rem;
    overflow: hidden;
    width: 100%;
    -webkit-mask-image: linear-gradient(to right, transparent, black 12%, black 88%, transparent);
    mask-image: linear-gradient(to right, transparent, black 12%, black 88%, transparent);
  }
  .mk-logos-roll-track {
    display: flex;
    align-items: center;
    gap: 2.5rem; /* matches .mk-logos-row column-gap */
    padding-inline-end: 2.5rem;
    width: max-content;
    animation: mk-logos-roll var(--mk-logos-roll-duration, 30s) linear infinite;
  }
  .mk-logos-roll:hover .mk-logos-roll-track,
  .mk-logos-roll:focus-within .mk-logos-roll-track { animation-play-state: paused; }
  /* vertical roll: column track in a fixed-height viewport, mask on
     the block edges instead */
  .mk-logos-roll[data-direction="y"] {
    height: var(--mk-logos-roll-height, 12rem);
    -webkit-mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent);
    mask-image: linear-gradient(to bottom, transparent, black 15%, black 85%, transparent);
  }
  .mk-logos-roll[data-direction="y"] .mk-logos-roll-track {
    flex-direction: column;
    margin-inline: auto;
    padding-inline-end: 0;
    padding-block-end: 2.5rem;
    animation-name: mk-logos-roll-y;
  }
  @keyframes mk-logos-roll {
    to { transform: translateX(-50%); }
  }
  @keyframes mk-logos-roll-y {
    to { transform: translateY(-50%); }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .mk-logo { transition: none; }
    /* static fallback: the roll collapses back into the plain wrapped
       row and the duplicated copy drops out so nothing renders twice */
    .mk-logos-roll,
    .mk-logos-roll[data-direction="y"] { height: auto; }
    .mk-logos-roll-track {
      animation: none;
      flex-wrap: wrap;
      justify-content: center;
      width: auto;
      padding-inline-end: 0;
      padding-block-end: 0;
    }
    .mk-logos-roll[data-direction="y"] .mk-logos-roll-track { flex-direction: row; animation: none; }
    .mk-logos-roll-track > [aria-hidden="true"] { display: none; }
  }
  @media (forced-colors: active) {
    .mk-logo { color: CanvasText; }
    /* masks hide content in forced-colors mode - show the full roll */
    .mk-logos-roll,
    .mk-logos-roll[data-direction="y"] { -webkit-mask-image: none; mask-image: none; }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub