Zum Inhalt springen
UI Kit
Accent

Logo-Generator

Platzhalter-Marken aus einem Seed für Logoreihen, Mockups und Fallstudien. Zwölf Familien von Zeichen, geseedete Wortmarken in der einen Schrift, Logos nebeneinander und gestapelt und App-Icons. Derselbe Name gibt immer dieselbe Marke, keine sieht aus wie eine echte Firma.

Eine von 3 Components in Platzhalter und Generatoren

5 Variants

Variants

Zeichen

Zwölf Zeichen aus zwölf Familien, jedes mit geseedeten Details. Umschalten zwischen einfarbig, Akzent und farbig, Hover dreht ein Zeichen.

Im Studio öffnen

Wort-Bild-Marken

Zeichen und Name nebeneinander, gestapelt oder nur das Wort. Die Zeichen federn herein und die Wörter werden beim Hereinscrollen scharf.

Im Studio öffnen

App-Icons

Das Zeichen auf einer runden Kachel mit weichem Licht, auf einem Startbildschirm mit Dock. Die Icons steigen nacheinander auf und geben beim Tippen nach.

Im Studio öffnen

Logo-Reihe

Ein ruhiges Raster für Kundenlogos auf der Landingpage, bevor die echten da sind. Gedämpft bis zum Hover, beim Hereinscrollen gezeichnet.

Im Studio öffnen

Markenblatt

Tippe einen Namen und bekomme ein kleines Markenblatt mit dem Logo, der gestapelten Fassung auf Dunkel und dem Icon in drei Größen. Andere Variante zeichnet eine neue Fassung.

Im Studio öffnen

Details

Was Logo-Generator mitbringt, aus Registry und Quellcode gelesen.

Bereich
Visuals
Variants
5
Version
1.0.0 · seit 1.1.0
Datei
components/ui/logo-generator.tsx
Bringt mit
utilskit
npm-Pakete
Keine
Sprachen
Texte kommen über Props

Props

Die Schnittstelle GeneratedLogoProps aus components/ui/logo-generator.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch.

  • namePflicht
    string

    The brand name. Also the seed unless seed is given.

  • seedoptional
    string
  • layoutoptional
    LogoLayout

    markwordmarklockupstackedapp-icon

  • toneoptional
    LogoTone

    monoaccentcolor

  • sizeoptional
    number

    Height of the mark in pixels, the text scales with it. Default 32.

  • animateoptional
    boolean

    Draw the mark in when it scrolls into view

  • markoptional
    number

    Pick the family of the mark yourself, 0 to 11. Default: from the seed.

Barrierefreiheit

Aus dem Quellcode der Component abgeleitet.

  • Setzt aria-label, damit Screenreader Zweck und Zustand ansagen.
  • Nutzt die Rolle img.
  • Kommt ohne Client-State aus. Der Server schreibt sie vollständig, sie ist auch ohne JavaScript lesbar.

Bewegung

  • Bei reduzierter Bewegung (prefers-reduced-motion) bleibt sie ruhig. 2 Stellen im Code schalten Bewegung dann ab.
  • Ohne Animationen, sie steht sofort ruhig da.

Quellcode

Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.

Datei anzeigen, 256 Zeilen
components/ui/logo-generator.tsx
'use client';

import { cn, round, useInView } from '@/lib/kit';

/*
 * Placeholder brands from a seed: a mark, a wordmark, or both. For logo
 * clouds, app mockups, case studies and anywhere a real logo is not there
 * yet. The same seed always gives the same brand. Nothing here resembles a
 * real company, the marks are built from simple geometry and the words are
 * set in the kit's one typeface.
 *
 *   mark       the symbol alone
 *   wordmark   the name in a seeded treatment (weight, case, tracking, a dot or a bar)
 *   lockup     symbol and name side by side
 *   stacked    symbol over name, centered
 *   app-icon   the symbol on a rounded tile with a soft light, like a home screen icon
 *
 * tone: mono follows the text color (for logo rows), accent uses the theme
 * accent, color picks one of the theme colors from the seed.
 */

export type LogoLayout = 'mark' | 'wordmark' | 'lockup' | 'stacked' | 'app-icon';
export type LogoTone = 'mono' | 'accent' | 'color';

export interface GeneratedLogoProps {
  /** The brand name. Also the seed unless seed is given. */
  name: string;
  seed?: string;
  layout?: LogoLayout;
  tone?: LogoTone;
  /** Height of the mark in pixels, the text scales with it. Default 32. */
  size?: number;
  /** Draw the mark in when it scrolls into view */
  animate?: boolean;
  /** Pick the family of the mark yourself, 0 to 11. Default: from the seed. */
  mark?: number;
  className?: string;
}

function hash(s: string) {
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return h >>> 0;
}
function random(seed: number) {
  let a = seed;
  return () => {
    a = (a + 0x6d2b79f5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

const COLORS = ['var(--primary)', 'var(--success)', 'var(--warning)', 'var(--info)', 'var(--primary-2)', 'var(--destructive)'];

/** The geometry of a mark on a 32 × 32 grid. Twelve families, each with seeded details. */
function markShapes(kind: number, r: () => number, letter: string): React.ReactNode {
  const v = Math.floor(r() * 3);
  switch (kind) {
    case 0: // sliced circle
      return (
        <>
          <path d="M16 3a13 13 0 1 1 0 26Z" fill="currentColor" />
          <path d="M16 3a13 13 0 0 0 0 26" fill="currentColor" opacity="0.4" />
          {v > 0 && <circle cx="16" cy="16" r={v === 1 ? 4 : 6} fill="var(--kl-bg, var(--background))" />}
        </>
      );
    case 1: // stacked bars
      return (
        <>
          {[0, 1, 2].map((i) => (
            <rect key={i} x={4 + (v === 1 ? i * 3 : 0)} y={5 + i * 8.5} width={v === 2 ? 24 - i * 6 : 24 - (v === 1 ? i * 3 : 0)} height="5.5" rx="2.75" fill="currentColor" opacity={1 - i * 0.25} />
          ))}
        </>
      );
    case 2: // triangle pair
      return (
        <>
          <path d="M3 27 14 6l11 21Z" fill="currentColor" />
          <path d="M14 27 22 12l8 15Z" fill="currentColor" opacity="0.45" />
        </>
      );
    case 3: // orbit
      return (
        <>
          <circle cx="16" cy="16" r="11.5" fill="none" stroke="currentColor" strokeWidth="3" />
          <circle cx={round(16 + Math.cos(-0.7 - v) * 11.5, 1)} cy={round(16 + Math.sin(-0.7 - v) * 11.5, 1)} r="4.5" fill="currentColor" />
        </>
      );
    case 4: // four petals
      return (
        <g fill="currentColor">
          {[0, 90, 180, 270].map((a, i) => (
            <path key={a} d="M16 16C16 9 20 4 26 4c0 6-5 12-10 12Z" transform={`rotate(${a + v * 15} 16 16)`} opacity={i % 2 ? 0.55 : 1} />
          ))}
        </g>
      );
    case 5: // dot grid
      return (
        <g fill="currentColor">
          {Array.from({ length: 9 }, (_, i) => {
            const x = 6 + (i % 3) * 10;
            const y = 6 + Math.floor(i / 3) * 10;
            const on = i === 4 || (i + v) % 2 === 0;
            return <circle key={i} cx={x} cy={y} r={on ? 3.6 : 2} opacity={on ? 1 : 0.4} />;
          })}
        </g>
      );
    case 6: // wave
      return (
        <>
          <path d="M3 13c4-5 7-5 10 0s6 5 10 0 6-5 6-5" fill="none" stroke="currentColor" strokeWidth="3.4" strokeLinecap="round" />
          <path d="M3 22c4-5 7-5 10 0s6 5 10 0 6-5 6-5" fill="none" stroke="currentColor" strokeWidth="3.4" strokeLinecap="round" opacity="0.45" />
        </>
      );
    case 7: // monogram in a square
      return (
        <>
          <rect x="3" y="3" width="26" height="26" rx={v === 0 ? 7 : v === 1 ? 13 : 2.5} fill="currentColor" />
          <text x="16" y="16" dy="0.35em" textAnchor="middle" fontSize="16" fontWeight="700" fill="var(--kl-bg, var(--background))">
            {letter}
          </text>
        </>
      );
    case 8: // arch
      return (
        <>
          <path d="M5 28V16a11 11 0 0 1 22 0v12h-6V16a5 5 0 0 0-10 0v12Z" fill="currentColor" />
          {v === 1 && <circle cx="16" cy="6" r="2.5" fill="currentColor" opacity="0.5" />}
        </>
      );
    case 9: // diamond fold
      return (
        <>
          <path d="M16 2 30 16 16 30 2 16Z" fill="currentColor" opacity="0.4" />
          <path d="M16 2 30 16 16 30Z" fill="currentColor" />
        </>
      );
    case 10: // sun rays
      return (
        <g fill="currentColor">
          <circle cx="16" cy="16" r="6" />
          {Array.from({ length: 8 }, (_, i) => (
            <rect key={i} x="15" y="2" width="2" height="6" rx="1" transform={`rotate(${i * 45} 16 16)`} opacity={i % 2 ? 0.5 : 1} />
          ))}
        </g>
      );
    default: // interlocking rings
      return (
        <g fill="none" stroke="currentColor" strokeWidth="3.2">
          <circle cx="12" cy="16" r="8" />
          <circle cx="20" cy="16" r="8" opacity="0.5" />
        </g>
      );
  }
}

/** Seeded word treatments, all in the one typeface */
function wordStyle(r: () => number) {
  const variant = Math.floor(r() * 5);
  return {
    weight: [600, 700, 500, 800, 600][variant],
    tracking: ['-0.03em', '-0.045em', '0.08em', '-0.02em', '0.01em'][variant],
    upper: variant === 2,
    lower: variant === 1 || variant === 4,
    dot: variant === 1 || variant === 3,
    bar: variant === 4,
  };
}

/**
 * A placeholder brand from a seed.
 *   <GeneratedLogo name="Brightwell" layout="lockup" tone="accent" size={32} />
 */
export function GeneratedLogo({ name, seed, layout = 'lockup', tone = 'mono', size = 32, animate = false, mark: family, className }: GeneratedLogoProps) {
  const h = hash(seed ?? name);
  const r = random(h);
  r();
  // The family comes from the high bits of the hash, which spread better over twelve than the first random draw
  const kind = family !== undefined ? ((Math.round(family) % 12) + 12) % 12 : (h >>> 20) % 12;
  const color = tone === 'mono' ? 'currentColor' : tone === 'accent' ? 'var(--primary)' : COLORS[Math.floor(r() * COLORS.length)];
  const letter = (name.trim()[0] ?? 'A').toUpperCase();
  const word = wordStyle(r);
  const [ref, inView] = useInView<HTMLSpanElement>();
  const hidden = animate && !inView;

  const mark = (box: number, onTile = false) => (
    <svg
      viewBox="0 0 32 32"
      aria-hidden
      className={cn('shrink-0 transition-[transform,opacity] duration-700 ease-[cubic-bezier(0.34,1.4,0.64,1)] motion-reduce:transition-none', hidden && 'rotate-[-30deg] scale-50 opacity-0')}
      style={{ width: box, height: box, color: onTile ? 'white' : color, ['--kl-bg' as string]: onTile ? color : undefined }}
    >
      {markShapes(kind, random(hash(`${seed ?? name}m`)), letter)}
    </svg>
  );
  // Case is a style, the text itself stays the name, so it reads and copies as written
  const caseClass = word.upper ? 'uppercase' : word.lower ? 'lowercase' : '';
  const wordmark = (fs: number) => (
    <span
      className={cn('relative inline-flex items-baseline whitespace-nowrap leading-none transition-[opacity,transform,filter]', caseClass, ' duration-700 ease-[var(--ease-kit)] motion-reduce:transition-none', hidden && 'translate-y-1 opacity-0 blur-[2px]')}
      style={{ fontSize: fs, fontWeight: word.weight, letterSpacing: word.tracking, color: tone === 'mono' ? 'currentColor' : 'var(--foreground)', transitionDelay: animate ? '150ms' : undefined }}
    >
      {name}
      {word.dot && <span style={{ color, marginLeft: '0.04em' }}>.</span>}
      {word.bar && <span aria-hidden className="absolute -bottom-[0.18em] left-0 h-[0.09em] w-[38%] rounded-full" style={{ background: color }} />}
    </span>
  );

  if (layout === 'app-icon') {
    return (
      <span ref={ref} role="img" aria-label={name} className={cn('relative inline-grid shrink-0 place-items-center overflow-hidden', className)} style={{ width: size, height: size, borderRadius: round(size * 0.24), background: tone === 'mono' ? 'var(--foreground)' : color }}>
        <span aria-hidden className="absolute inset-0 bg-[linear-gradient(160deg,rgb(255_255_255/0.28),transparent_55%)]" />
        <span aria-hidden className="absolute inset-0 rounded-[inherit] shadow-[inset_0_1px_0_rgb(255_255_255/0.35),inset_0_-1px_0_rgb(0_0_0/0.12)]" />
        <span className="relative" style={{ ['--kl-bg' as string]: tone === 'mono' ? 'var(--foreground)' : color }}>
          {mark(round(size * 0.56), true)}
        </span>
      </span>
    );
  }
  if (layout === 'mark') {
    return (
      <span ref={ref} role="img" aria-label={name} className={cn('inline-flex', className)}>
        {mark(size)}
      </span>
    );
  }
  if (layout === 'wordmark') {
    return (
      <span ref={ref} className={cn('inline-flex', className)}>
        {wordmark(round(size * 0.82))}
      </span>
    );
  }
  if (layout === 'stacked') {
    return (
      <span ref={ref} className={cn('inline-flex flex-col items-center', className)} style={{ gap: round(size * 0.28) }}>
        {mark(size)}
        {wordmark(round(size * 0.5))}
      </span>
    );
  }
  return (
    <span ref={ref} className={cn('inline-flex items-center', className)} style={{ gap: round(size * 0.3) }}>
      {mark(size)}
      {wordmark(round(size * 0.62))}
    </span>
  );
}

/** Names that sound like small, believable companies. Combine with a seed for endless placeholders. */
export const PLACEHOLDER_BRANDS = ['Brightwell', 'Fernhall', 'Kestrel', 'Lumora', 'Northbay', 'Oakline', 'Quillo', 'Rivetta', 'Solace', 'Tidewell', 'Veridian', 'Wrenfield', 'Halden', 'Marisol', 'Corvana', 'Ostara'];

Häufige Fragen

Wie installiere ich Logo-Generator?

Mit aiw add @ai-website/logo-generator. Der Befehl legt die Datei components/ui/logo-generator.tsx in dein Projekt, der Code gehört danach dir. Weitere npm-Pakete braucht sie nicht.

Welche Varianten hat Logo-Generator?

5 Varianten, Zeichen, Wort-Bild-Marken, App-Icons, Logo-Reihe und Markenblatt.

Kann ein KI-Assistent Logo-Generator einbauen?

Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Logo-Generator mit aiw_components und installieren sie mit aiw_add @ai-website/logo-generator.

Kann ich Logo-Generator ohne Code anpassen?

Ja, im Studio. Alle 5 Varianten lassen sich dort bearbeiten, Texte, Listen und Schalter. Das Studio schreibt den passenden GeneratedLogo-Code zum Kopieren.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert