Simple footer block

One row with the copyright on the left and Privacy, Terms and Status on the right, with an Operational SvBadge on Status. The row stacks on phones.

A live, editable Svelte 5 component example from the SvGrid gallery (Blocks). Read the SvGrid UI component docs for the full API.

About this example

A one-row footer for Svelte 5 built from the SvGrid UI kit: the copyright year on the left and Privacy, Terms and Status links on the right, with a green Operational SvBadge on the Status link. On phones the row stacks with the links above the copyright.

Simple footer block - the smallest useful site footer: one row with the copyright year on the left and three links on the right, under the end of an app page. The Status link carries a green Operational SvBadge. On phones the row stacks with the links above the copyright. Pure UI-kit, --sg-* themed.

Headless: createSimpleFooter() below owns the links, the copyright line and the aria-current wiring with no markup. The component under it is one styled render of that core; import createSimpleFooter from this file (or npx @svgrid/ui add footers-simple --headless for the core alone) to draw your own.

Imports, features and API used

Imports: @svgrid/grid

Frequently asked questions

How do I change the links?

Pass links to createSimpleFooter(), an array of { label, href, status? }. A link with status: true gets a green SvBadge reading Operational after its label.

Does it mark the current page?

Yes, when you pass currentPath. linkProps(link) sets aria-current="page" on the link whose href matches it, for example page.url.pathname from $app/state.

How does it change on a phone?

Below 560 px the footer switches to a reversed column, so the three links sit above the copyright, both aligned left.

Related documentation

Related articles

Source code (595-block-footers-simple.svelte)

<script lang="ts" module>
  /**
   * Simple footer block - the smallest useful site footer: one row with the
   * copyright year on the left and three links on the right, under the end
   * of an app page. The Status link carries a green Operational SvBadge.
   * On phones the row stacks with the links above the copyright.
   * Pure UI-kit, --sg-* themed.
   *
   * Headless: createSimpleFooter() below owns the links, the copyright line
   * and the aria-current wiring with no markup. The component under it is one
   * styled render of that core; import createSimpleFooter from this file (or
   * `npx @svgrid/ui add footers-simple --headless` for the core alone) to
   * draw your own.
   */
  type FooterLink = { label: string; href: string; status?: boolean }
  type SimpleFooterOptions = {
    links?: FooterLink[]
    company?: string
    /** The current path, so the matching link gets aria-current="page". */
    currentPath?: string
  }

  export function createSimpleFooter(opts: SimpleFooterOptions = {}) {
    // Defaults are the demo sample data; pass your own through opts.
    const year = new Date().getFullYear()
    return {
      links: opts.links ?? [
        { label: 'Privacy', href: '/legal/privacy' },
        { label: 'Terms', href: '/legal/terms' },
        { label: 'Status', href: '/status', status: true },
      ],
      year,
      copyright: `© ${year} ${opts.company ?? 'Fieldline Software Ltd.'}`,
      /** Spread on each link's <a>. */
      linkProps: (link: FooterLink) => ({
        href: link.href,
        'aria-current': link.href === opts.currentPath ? ('page' as const) : undefined,
      }),
    }
  }

  export type { FooterLink, SimpleFooterOptions };
</script>

<script lang="ts">
  import { SvBadge } from '@svgrid/grid'

  const footer = createSimpleFooter()
</script>

<div class="sv-footer-simple">
  <div class="sv-footer-simple-site">
    <main class="sv-footer-simple-page">
      <h2>Invoices</h2>
      <div class="sv-footer-simple-rows" aria-hidden="true">
        {#each [78, 64, 82, 58] as w, i (i)}<i style:--w="{w}%"></i>{/each}
      </div>
    </main>

    <footer class="sv-footer-simple-footer">
      <p>{footer.copyright}</p>
      <nav aria-label="Legal and status">
        <ul>
          {#each footer.links as link (link.href)}
            <li>
              <a {...footer.linkProps(link)}>
                {link.label}
                {#if link.status}<SvBadge variant="success" dot size="sm">Operational</SvBadge>{/if}
              </a>
            </li>
          {/each}
        </ul>
      </nav>
    </footer>
  </div>
</div>

<style>
  .sv-footer-simple {
    width: 100%; box-sizing: border-box; padding: 32px 24px;
    background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); font-family: var(--sg-font, inherit);
  }
  .sv-footer-simple-site {
    max-width: 960px; margin: 0 auto; overflow: hidden;
    border: 1px solid var(--sg-border, #e2e8f0); border-radius: var(--sg-radius-lg, 12px);
  }
  .sv-footer-simple-page { padding: 28px 28px 32px; }
  .sv-footer-simple-page h2 { margin: 0 0 18px; font-size: 22px; font-weight: 700; letter-spacing: -.01em; }
  .sv-footer-simple-rows { display: flex; flex-direction: column; }
  .sv-footer-simple-rows i { display: flex; align-items: center; height: 40px; border-top: 1px solid var(--sg-border, #e2e8f0); }
  .sv-footer-simple-rows i::before {
    content: ''; display: block; width: var(--w); height: 8px; border-radius: 4px;
    background: color-mix(in srgb, var(--sg-fg, #0f172a) 9%, var(--sg-bg, #fff));
  }

  .sv-footer-simple-footer {
    display: flex; align-items: center; justify-content: space-between; gap: 12px 24px;
    padding: 18px 28px; border-top: 1px solid var(--sg-border, #e2e8f0);
  }
  .sv-footer-simple-footer p { margin: 0; font-size: 13px; color: var(--sg-muted, #64748b); }
  .sv-footer-simple-footer ul { list-style: none; margin: 0; padding: 0; display: flex; gap: 24px; }
  .sv-footer-simple-footer a { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--sg-muted, #64748b); text-decoration: none; }
  .sv-footer-simple-footer a:hover { color: var(--sg-fg, #0f172a); text-decoration: underline; text-underline-offset: 3px; }
  .sv-footer-simple-footer a:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--sg-accent, #2563eb)); outline-offset: 2px; border-radius: 3px; }

  @media (max-width: 560px) {
    .sv-footer-simple { padding: 16px 12px; }
    .sv-footer-simple-page { padding: 20px 16px 24px; }
    .sv-footer-simple-footer { flex-direction: column-reverse; align-items: flex-start; padding: 18px 16px; }
  }
</style>

View this example on GitHub

More Blocks examples

  • Login block - A two-column authentication page: a branded gradient panel beside a sign-in SvCard with social SvButtons, SvTextInput + SvPasswordInput (reveal), remember-me SvCheckBox, an "or" SvDivider and forgot-password link. Drops into any route as-is.
  • Sign-up block - A centered account-creation card: two-up name fields, work email, a password with the live strength meter, a plan SvSegmented switch, terms SvCheckBox and a benefits rail. Submit fires a promise toast. Pure UI-kit.
  • Two-factor / OTP block - The verification step of a sign-in flow: SvOtpInput (6 digits, auto-advance + onComplete), a resend cooldown timer, and the waiting -> verifying -> verified states. Enter 000000 to see the error path.
  • Analytics dashboard block - A full overview page: a range SvSegmented + actions header, a KPI grid (SvStat + SvSparkline), a revenue bar panel, a recent-activity SvTimeline and a top-products list with SvProgress bars. No grid dependency.
  • App shell block - A complete application layout: a SvNavPane sidebar with sections + badges, a top bar with SvBreadcrumb, search and an account SvMenu, and a routed content area. Nav swaps the page; the toggle collapses the rail to an icon rail.