# 푸터 메타 줄

푸터 맨 아래의 피드 링크 줄과 크레딧 줄이에요.

- Type: component
- Theme: base
- Tags: arc, component, footer, feed, rss, credits, meta
- English title: Footer meta
- URL: https://dsui.vercel.app/items/footer-meta

## Controls

| Key | Type | Label | Default |
| --- | --- | --- | --- |
| copyright | text | 저작권 문구 | © 2026 devs7udio |

## Source

### footer-meta.module.css

```css
.meta { display: flex; flex-direction: column; }
.feeds { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding-block: var(--space-4); border-block-end: 1px solid var(--border-subtle); }
.feedItem { display: inline-flex; align-items: center; gap: var(--space-2) var(--space-3); }
.divider { width: 1px; height: var(--space-4); background: var(--border); }
.feed { display: inline-flex; align-items: center; gap: var(--space-1); border-radius: var(--radius-pill); padding: var(--space-1) var(--space-2); color: var(--text-secondary); font-size: var(--text-sm); text-decoration: none; transition: color var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard); }
.feed svg { flex: none; }
@media (hover: hover) and (pointer: fine) { .feed:hover { background: var(--surface-muted); color: var(--foreground); } }
.feed:active { background: var(--surface-sunken); color: var(--foreground); }

.credits { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); padding-block: var(--space-4); }
/* Smaller than the feed row, since nobody comes to a page looking for this. The colour still has to hold its contrast. */
.copyright { color: var(--text-secondary); font-size: var(--text-xs); }
.creditList { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.credit { color: var(--text-secondary); font-size: var(--text-xs); }
.creditName { color: var(--foreground); }
.creditLink { color: var(--foreground); text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .creditLink:hover { text-decoration: underline; text-underline-offset: 3px; } }
@media (prefers-reduced-motion: reduce) { .feed { transition: none; } }

```

### footer-meta.tsx

```tsx
import type { HTMLAttributes, ReactNode } from "react";
import { ArcLink } from "../lib/arc-provider";
import styles from "./footer-meta.module.css";

export interface FooterFeedLink {
  href: string;
  label: string;
  /** A small decorative mark before the label. */
  icon?: ReactNode;
  /** Leaves the link as a plain anchor: a file, a feed, another origin. */
  plain?: boolean;
}

export interface FooterCredit {
  /** What is being credited, e.g. "Powered by". */
  label: string;
  /** Who, e.g. "Arc". */
  name: ReactNode;
  href?: string;
}

export interface FooterMetaProps extends HTMLAttributes<HTMLDivElement> {
  /** Machine-readable things: a feed, llms.txt, a sitemap. */
  feeds?: FooterFeedLink[];
  /** The copyright line, at the start of the credits row. */
  copyright?: ReactNode;
  /** What the site owes to others. */
  credits?: FooterCredit[];
  /** The accessible name of the feed row. */
  feedLabel?: string;
}

/**
 * The two quiet rows at the very bottom of a footer: what machines can read, and what the site owes to other people.
 * They are separated because they are read by different audiences — one by a feed reader or a crawler, the other by a
 * person — and keeping them in one row makes both harder to find.
 *
 * Everything here is 12–13px. Bigger and it competes with the content; the contrast still has to hold, so the size
 * drops but the colour does not.
 */
export function FooterMeta({ className, feeds, copyright, credits, feedLabel = "피드와 파일", ...props }: FooterMetaProps) {
  if (!feeds?.length && !copyright && !credits?.length) return null;
  return (
    <div {...props} className={[styles.meta, className].filter(Boolean).join(" ")}>
      {feeds?.length ? (
        <nav aria-label={feedLabel} className={styles.feeds}>
          {feeds.map((feed, index) => (
            <span key={feed.href} className={styles.feedItem}>
              {index > 0 ? <span aria-hidden="true" className={styles.divider} /> : null}
              {feed.plain ? (
                <a href={feed.href} className={styles.feed}>{feed.icon}{feed.label}</a>
              ) : (
                <ArcLink href={feed.href} className={styles.feed}>{feed.icon}{feed.label}</ArcLink>
              )}
            </span>
          ))}
        </nav>
      ) : null}

      {copyright || credits?.length ? (
        <div className={styles.credits}>
          {copyright ? <span className={styles.copyright}>{copyright}</span> : null}
          {credits?.length ? (
            <span className={styles.creditList}>
              {credits.map(credit => (
                <span key={credit.label} className={styles.credit}>
                  {credit.label}{" "}
                  {credit.href
                    ? <a href={credit.href} className={styles.creditLink} rel="noreferrer">{credit.name}</a>
                    : <span className={styles.creditName}>{credit.name}</span>}
                </span>
              ))}
            </span>
          ) : null}
        </div>
      ) : null}
    </div>
  );
}

FooterMeta.displayName = "FooterMeta";

export default FooterMeta;

```
