Theme
On this page (5)
Component Skill — components/newsletter/component-skill.md

Native basis

<form> + <input type="email" required> + submit Button. The browser enforces the email format; enterkeyhint="send" and autocomplete="email" tune mobile keyboards - zero validation JS.

Web Platform APIs

<input type="email">autocompleteenterkeyhintContainer queries

Classes

.mk-newsletter.mk-newsletter-layout.mk-newsletter-copy.mk-newsletter-title.mk-newsletter-desc.mk-newsletter-form-col.mk-newsletter-form.mk-newsletter-note

§Default

Stacked on narrow widths, inline at 30rem+.

§Card with social proof

data-variant='card': centered on a surface with a primary glow - an icon, the form, and social proof from an Avatar group.

§Gradient banner

data-variant='banner': a primary gradient strip - copy left, form right; the input and button invert onto it.

§Split with topics

.mk-newsletter-split: an image beside the form; Checkboxes in a fieldset (.mk-newsletter-topics) pick the lists - native form fields, the browser validates the email.

§CSS view file

/* -- Newsletter (marketing) ----------------------------------------
   Two-column sign-up band: copy left, inline email form right. The
   form is a real <form> + <input type="email"> - browser validation
   and the native keyboard work for free. */
@layer components {
  .mk-newsletter {
    container: mk-newsletter / inline-size;
    width: 100%;
    max-width: 80rem;
    margin-inline: auto;
    padding-inline: 1.25rem;
  }
  .mk-newsletter-layout {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 3rem;
  }
  .mk-newsletter-copy {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
    width: 100%;
    max-width: 48rem;
    text-align: left;
  }
  .mk-newsletter-title {
    line-height: 1.15;
    margin: 0;
    font-size: 1.875rem;
    font-weight: 500;
    letter-spacing: -0.025em;
    text-wrap: balance;
  }
  .mk-newsletter-desc { margin: 0; max-width: 42rem; color: var(--muted-foreground); font-size: 1rem; text-wrap: pretty; }
  .mk-newsletter-form-col { display: flex; flex-direction: column; gap: 0.5rem; }
  .mk-newsletter-form { display: flex; flex-direction: column; gap: 0.5rem; }
  .mk-newsletter-form .input { width: 100%; }
  .mk-newsletter-form .btn { width: 100%; }
  /* note and error sit just inside the input's edge */
  .mk-newsletter-note,
  .mk-newsletter-error {
    padding-inline-start: 0.375rem;
    font-size: 0.75rem;
  }
  .mk-newsletter-note { color: var(--muted-foreground); }
  /* the error shows only after an attempt - :user-invalid, no script */
  .mk-newsletter-error {
    display: none;
    align-items: center;
    gap: 0.25rem;
    color: var(--destructive);
    font-weight: 500;
    & svg { width: 0.875rem; height: 0.875rem; flex: none; }
  }
  .mk-newsletter-form-col:has(input:user-invalid) {
    & .mk-newsletter-error { display: flex; }
    & .mk-newsletter-note { display: none; }
  }
  @container mk-newsletter (min-width: 30rem) {
    .mk-newsletter-layout { flex-direction: row; }
    .mk-newsletter-form-col { align-self: flex-end; }
    .mk-newsletter-form { flex-direction: row; }
    .mk-newsletter-form .input { width: fit-content; }
    .mk-newsletter-form .btn { width: auto; }
  }
  @container mk-newsletter (min-width: 52rem) {
    .mk-newsletter-layout { gap: 4rem; }
    .mk-newsletter-title { font-size: 2.25rem; }
    .mk-newsletter-form-col { gap: 0.75rem; }
  }
  /* card: centered on a surface - an icon, the form, social proof */
  .mk-newsletter[data-variant="card"] .mk-newsletter-layout {
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
    max-width: 36rem;
    margin-inline: auto;
    padding: 2.5rem 2rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    background:
      radial-gradient(80% 60% at 50% 0%, color-mix(in oklch, var(--primary) 10%, transparent), transparent 70%),
      var(--card);
    text-align: center;
    & .mk-newsletter-copy { align-items: center; text-align: center; }
    & .mk-newsletter-form-col { align-self: stretch; }
    /* centered card: the input + button row and the note/error under it center too */
    & :is(.mk-newsletter-note, .mk-newsletter-error) { justify-content: center; padding-inline-start: 0; text-align: center; }
  }
  @container mk-newsletter (min-width: 30rem) {
    .mk-newsletter[data-variant="card"] .mk-newsletter-form { justify-content: center; }
    .mk-newsletter[data-variant="card"] .mk-newsletter-form .input { flex: 0 1 18rem; width: auto; }
  }
  .mk-newsletter-icon {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--radius-lg);
    background: var(--primary);
    color: var(--primary-foreground);
    & svg { width: 1.375rem; height: 1.375rem; }
  }
  .mk-newsletter-proof {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    color: var(--muted-foreground);
    font-size: 0.8125rem;
    & strong { color: var(--foreground); }
  }
  /* banner: a gradient strip - copy and form on one row */
  .mk-newsletter[data-variant="banner"] .mk-newsletter-layout {
    align-items: center;
    padding: 1.75rem 2rem;
    border-radius: var(--radius-xl);
    background: linear-gradient(120deg, var(--primary), color-mix(in oklch, var(--chart-2) 70%, var(--primary)));
    color: var(--primary-foreground);
    & .mk-newsletter-title { font-size: 1.5rem; }
    & .mk-newsletter-desc,
    & .mk-newsletter-note { color: color-mix(in oklch, var(--primary-foreground) 75%, transparent); }
    & .input { background: color-mix(in oklch, var(--primary-foreground) 92%, transparent); color: #111; }
    & .btn { background: var(--primary-foreground); color: var(--primary); }
  }
  /* topics: pick lists with Checkboxes */
  .mk-newsletter-topics {
    display: grid;
    gap: 0.5rem;
    margin: 0;
    padding: 0;
    border: 0;
    & legend { margin-block-end: 0.5rem; font-size: 0.875rem; font-weight: 600; }
  }
  .mk-newsletter-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
    gap: 2rem 3rem;
    align-items: center;
    & img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; border-radius: var(--radius-xl); }
    & form { display: grid; gap: 1rem; }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (forced-colors: active) {
    .mk-newsletter[data-variant="banner"] .mk-newsletter-layout { border: 1px solid CanvasText; }
  }
}

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