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
Variants
Details
Was Logo-Generator mitbringt, aus Registry und Quellcode gelesen.
- Kategorie
- Platzhalter und Generatoren
- 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
- Mit KI bauen
- Per MCP in Claude, Cursor, Copilot und Codex
Props
Die Schnittstelle GeneratedLogoProps aus components/ui/logo-generator.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch.
namePflichtstringThe brand name. Also the seed unless seed is given.
seedoptionalstringlayoutoptionalLogoLayoutmarkwordmarklockupstackedapp-icon
toneoptionalLogoTonemonoaccentcolor
sizeoptionalnumberHeight of the mark in pixels, the text scales with it. Default 32.
animateoptionalbooleanDraw the mark in when it scrolls into view
markoptionalnumberPick 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
'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.