Press Kit Brand Assets
blockmarketingComprehensive brand guidelines and press kit hub with official logo downloads in vector SVG/4K PNG/WebP, OKLCH brand palette tokens, executive leadership headshots (300 DPI), and press contact resources.
Installation
$pnpm dlx uipkge-ng@latest add press-kit-brand-assets$npx uipkge-ng@latest add press-kit-brand-assets$yarn dlx uipkge-ng@latest add press-kit-brand-assets$bunx uipkge-ng@latest add press-kit-brand-assetsManifest:
https://uipkge.dev/r/angular/press-kit-brand-assets.jsonInstalls to:src/app/components/blocks/press-kit-brand-assets/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
LogoAssetinterface LogoAsset {
id: string
title: string
category: string
description: string
dimensions: string
bestFor: string
theme: 'light' | 'dark' | 'adaptive'
formats: { label: string; ext: string; size: string }[]
}BrandColorinterface BrandColor {
id: string
name: string
hex: string
oklch: string
rgb: string
role: string
contrast: string
usage: string
bgClass: string
textDark?: boolean
}Executiveinterface Executive {
id: string
name: string
role: string
bio: string
avatarUrl?: string
initials: string
fileSize: string
dimensions: string
topics: string[]
socials: { platform: 'x' | 'linkedin' | 'github'; url: string }[]
}BrandGuidelineRuleinterface BrandGuidelineRule {
type: 'do' | 'dont'
text: string
}Theming
CSS custom properties referenced in this item. Override any of them in your :root or per-element to retheme.
--borderFiles installed (2)
src/app/components/blocks/press-kit-brand-assets/press-kit-brand-assets.component.ts44.3 kB
// Brand guidelines and press kit hub: official logo downloads (SVG/4K PNG/WebP), // OKLCH brand palette tokens, executive leadership headshots (300 DPI), and press // contact resources. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { Check, CheckCircle2, Copy, Download, ExternalLink, FileArchive, FileCode, FileImage, FileText, Globe, Info, Layers, LucideAngularModule, Mail, Palette, ShieldCheck, User, XCircle, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' export interface LogoAsset { id: string title: string category: string description: string dimensions: string bestFor: string theme: 'light' | 'dark' | 'adaptive' formats: { label: string; ext: string; size: string }[] } export interface BrandColor { id: string name: string hex: string oklch: string rgb: string role: string contrast: string usage: string bgClass: string textDark?: boolean } export interface Executive { id: string name: string role: string bio: string avatarUrl?: string initials: string fileSize: string dimensions: string topics: string[] socials: { platform: 'x' | 'linkedin' | 'github'; url: string }[] } export interface BrandGuidelineRule { type: 'do' | 'dont' text: string } const DEFAULT_LOGOS: LogoAsset[] = [ { id: 'horizontal-primary', title: 'Primary Horizontal Logo', category: 'Full Wordmark', description: 'The standard lockup combining the faceted block logomark with horizontal typography and .dev badge.', dimensions: '3840 × 1080 px', bestFor: 'Light surfaces, navigation bars, marketing hero headers, and pitch decks.', theme: 'light', formats: [ { label: 'Vector SVG', ext: 'svg', size: '14 KB' }, { label: '4K PNG (Alpha)', ext: 'png', size: '280 KB' }, { label: 'Modern WebP', ext: 'webp', size: '94 KB' }, ], }, { id: 'app-icon-mark', title: 'Square App Icon Mark', category: 'Logomark Symbol', description: 'Standalone geometric prism icon optimized for square aspect ratios and high-density icons.', dimensions: '1024 × 1024 px', bestFor: 'Favicons, desktop dock icons, app store assets, and square social avatars.', theme: 'adaptive', formats: [ { label: 'Vector SVG', ext: 'svg', size: '8 KB' }, { label: 'High-Res PNG', ext: 'png', size: '165 KB' }, { label: 'Icon WebP', ext: 'webp', size: '62 KB' }, ], }, { id: 'monochrome-dark', title: 'Dark Mode Monochromatic Logo', category: 'Dark Backgrounds', description: 'Single-tone pure-white silhouette engineered for deep slate surfaces and obsidian backgrounds.', dimensions: '3840 × 1080 px', bestFor: 'Developer terminals, dark theme dashboards, midnight wallpapers, and video overlays.', theme: 'dark', formats: [ { label: 'Vector SVG', ext: 'svg', size: '12 KB' }, { label: '4K PNG (White)', ext: 'png', size: '240 KB' }, { label: 'Dark WebP', ext: 'webp', size: '88 KB' }, ], }, { id: 'monochrome-light', title: 'Light Mode Inverted Logo', category: 'Print & Documents', description: 'High-density obsidian monochrome lockup for single-ink printing and high-contrast editorial media.', dimensions: '3840 × 1080 px', bestFor: 'Whitepapers, physical print collateral, invoices, PDF documents, and newsprint.', theme: 'light', formats: [ { label: 'Vector SVG', ext: 'svg', size: '12 KB' }, { label: '4K PNG (Black)', ext: 'png', size: '235 KB' }, { label: 'Print WebP', ext: 'webp', size: '85 KB' }, ], }, ] const DEFAULT_COLORS: BrandColor[] = [ { id: 'primary-emerald', name: 'Primary Emerald', hex: '#10B981', oklch: 'oklch(0.696 0.170 162.48)', rgb: 'rgb(16, 185, 129)', role: 'Primary Interactive Brand Color', contrast: 'WCAG AAA 7.4:1', usage: 'Primary brand color for CTAs, active pills, key highlights, and success indicators.', bgClass: 'bg-[#10B981]', textDark: true, }, { id: 'midnight-slate', name: 'Midnight Slate', hex: '#0F172A', oklch: 'oklch(0.208 0.042 265.75)', rgb: 'rgb(15, 23, 42)', role: 'Dark Neutral Surface Base', contrast: 'WCAG AAA 16.2:1', usage: 'Core neutral background and high-contrast dark surface foundations across dark layouts.', bgClass: 'bg-[#0F172A]', textDark: false, }, { id: 'accent-blue', name: 'Accent Blue', hex: '#3B82F6', oklch: 'oklch(0.623 0.214 259.80)', rgb: 'rgb(59, 130, 246)', role: 'Secondary Interactive Accent', contrast: 'WCAG AA 4.8:1', usage: 'Secondary accent for active links, information badges, focus rings, and secondary metrics.', bgClass: 'bg-[#3B82F6]', textDark: true, }, { id: 'background-base', name: 'Background Base', hex: '#F8FAFC', oklch: 'oklch(0.985 0.002 247.80)', rgb: 'rgb(248, 250, 252)', role: 'Light Neutral Surface Fills', contrast: 'WCAG AAA 19.5:1', usage: 'Clean ambient light surface foundation, card backgrounds, and elevated container fills.', bgClass: 'bg-[#F8FAFC]', textDark: true, }, { id: 'amber-highlight', name: 'Amber Highlight', hex: '#F59E0B', oklch: 'oklch(0.769 0.188 70.08)', rgb: 'rgb(245, 158, 11)', role: 'Warning & Spotlight Accent', contrast: 'WCAG AAA 8.1:1', usage: 'Warm highlight for warnings, pro tier tags, system notices, and featured sponsor badges.', bgClass: 'bg-[#F59E0B]', textDark: true, }, ] const DEFAULT_EXECUTIVES: Executive[] = [ { id: 'elena-rostova', name: 'Elena Rostova', role: 'Chief Executive Officer & Co-Founder', bio: 'Pioneered component registry architecture and headless design systems. Former principal architect at NextGen UI. Leading UIPKGE’s mission to make design-engineering frictionless for global software teams.', avatarUrl: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=400&auto=format&fit=crop', initials: 'ER', fileSize: '12.4 MB', dimensions: '4000 × 5000 px · 300 DPI', topics: ['Component Architecture', 'Design Systems', 'Open-Source Sustainability', 'Developer Experience'], socials: [ { platform: 'x', url: 'https://x.com/elenarostova' }, { platform: 'linkedin', url: 'https://linkedin.com/in/elenarostova' }, { platform: 'github', url: 'https://github.com/elenarostova' }, ], }, { id: 'marcus-vance', name: 'Marcus Vance', role: 'Chief Technology Officer & Co-Founder', bio: 'Specializes in multi-framework compiler tooling, zero-runtime tokens, and high-performance WebAssembly render pipelines. Previously led frontend infrastructure and UI performance engineering at CloudScale.', avatarUrl: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=400&auto=format&fit=crop', initials: 'MV', fileSize: '11.8 MB', dimensions: '4000 × 5000 px · 300 DPI', topics: ['Web Performance', 'AST Tooling', 'Tailwind v4 Engine', 'Reka UI Headless Core'], socials: [ { platform: 'x', url: 'https://x.com/marcusvance' }, { platform: 'linkedin', url: 'https://linkedin.com/in/marcusvance' }, { platform: 'github', url: 'https://github.com/marcusvance' }, ], }, ] const DEFAULT_GUIDELINES: BrandGuidelineRule[] = [ { type: 'do', text: 'Maintain minimum clear space equal to 50% of the logomark height around all sides.' }, { type: 'do', text: 'Use the official OKLCH and Hex values when reproducing brand swatches in digital media.' }, { type: 'do', text: 'Use high-contrast monochromatic variants for single-color print, laser etching, or stamps.' }, { type: 'dont', text: 'Do not stretch, distort, rotate, or alter the aspect ratio of official logo assets.' }, { type: 'dont', text: 'Do not add drop shadows, outer glows, strokes, or unapproved gradients to the mark.' }, { type: 'dont', text: 'Do not place the full-color primary mark on low-contrast or visually busy backgrounds.' }, ] type PressKitTab = 'all' | 'logos' | 'colors' | 'leadership' | 'press' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-press-kit-brand-assets, [ui-press-kit-brand-assets]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, UiSeparatorComponent, ], template: ` <div data-slot="press-kit-brand-assets" [class]="rootClass"> <div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8 lg:py-16"> <!-- Header Hero Section --> <div class="border-border bg-card relative overflow-hidden rounded-2xl border p-6 shadow-xs sm:p-10"> <div class="relative z-10 flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between"> <div class="max-w-3xl space-y-4"> <div class="flex flex-wrap items-center gap-2"> <span ui-badge variant="outline" class="border-primary/30 bg-primary/10 text-primary gap-1.5 font-medium" > <lucide-icon [img]="Palette" class="size-3.5 shrink-0" /> <span>Media & Brand Hub</span> </span> <span ui-badge variant="secondary" class="font-mono text-xs"> {{ version }} </span> <span class="text-muted-foreground text-xs">Updated {{ lastUpdated }}</span> </div> <h1 class="text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl"> {{ title }} </h1> <p class="text-muted-foreground max-w-2xl text-base leading-relaxed sm:text-lg"> {{ subtitle }} </p> <div class="text-muted-foreground flex flex-wrap items-center gap-4 pt-1 text-xs"> <div class="flex items-center gap-1.5"> <lucide-icon [img]="ShieldCheck" class="text-primary size-4 shrink-0" /> <span>Editorial Use License (CC BY-NC 4.0)</span> </div> <ui-separator orientation="vertical" class="hidden h-3 sm:block" /> <div class="flex items-center gap-1.5"> <lucide-icon [img]="FileArchive" class="text-muted-foreground size-4 shrink-0" /> <span>24 Verified Brand Assets</span> </div> <ui-separator orientation="vertical" class="hidden h-3 sm:block" /> <div class="flex items-center gap-1.5"> <lucide-icon [img]="Globe" class="text-muted-foreground size-4 shrink-0" /> <span>Vector & 300 DPI Print Ready</span> </div> </div> </div> <div class="flex flex-col gap-3 sm:min-w-[280px] lg:flex-col"> <button ui-button aria-label="Download complete press kit" size="lg" class="focus-visible:ring-ring h-auto min-h-10 w-full gap-2 py-2.5 text-center font-semibold whitespace-normal shadow-sm focus-visible:ring-2" (click)="handleDownload('all-zip')" > @if (downloadedKey() === 'all-zip') { <lucide-icon [img]="Check" class="size-4 shrink-0" /> } @else { <lucide-icon [img]="Download" class="size-4 shrink-0" /> } <span> {{ downloadedKey() === 'all-zip' ? 'Press Kit Downloaded' : 'Download Complete Press Kit (.ZIP · ' + pressKitZipSize + ')' }} </span> </button> <div class="grid grid-cols-2 gap-2"> <button ui-button variant="outline" size="sm" class="h-auto min-h-8 w-full gap-1.5 text-center text-xs whitespace-normal" (click)="copyToClipboard('email-header', pressEmail)" > @if (copiedKey() === 'email-header') { <lucide-icon [img]="Check" class="text-primary size-3.5 shrink-0" /> } @else { <lucide-icon [img]="Mail" class="size-3.5 shrink-0" /> } <span>{{ copiedKey() === 'email-header' ? 'Copied' : 'Press Desk' }}</span> </button> <button ui-button variant="outline" size="sm" class="h-auto min-h-8 w-full gap-1.5 text-center text-xs whitespace-normal" (click)="copyToClipboard('boilerplate-header', boilerplateText)" > @if (copiedKey() === 'boilerplate-header') { <lucide-icon [img]="Check" class="text-primary size-3.5 shrink-0" /> } @else { <lucide-icon [img]="Copy" class="size-3.5 shrink-0" /> } <span>{{ copiedKey() === 'boilerplate-header' ? 'Copied' : 'Boilerplate' }}</span> </button> </div> </div> </div> </div> <!-- Quick Section Anchor Tabs (Default Variant) --> @if (variant === 'default') { <div class="mt-8 flex items-center gap-2 overflow-x-auto pb-2"> <button ui-button [variant]="activeTab() === 'all' ? 'default' : 'ghost'" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="activeTab.set('all')" > <lucide-icon [img]="Layers" class="size-3.5 shrink-0" /> <span>All Assets</span> </button> <button ui-button [variant]="activeTab() === 'logos' ? 'default' : 'ghost'" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="activeTab.set('logos')" > <lucide-icon [img]="FileImage" class="size-3.5 shrink-0" /> <span>Logos & Marks ({{ activeLogos().length }})</span> </button> <button ui-button [variant]="activeTab() === 'colors' ? 'default' : 'ghost'" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="activeTab.set('colors')" > <lucide-icon [img]="Palette" class="size-3.5 shrink-0" /> <span>Color Palette ({{ activeColors().length }})</span> </button> <button ui-button [variant]="activeTab() === 'leadership' ? 'default' : 'ghost'" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="activeTab.set('leadership')" > <lucide-icon [img]="User" class="size-3.5 shrink-0" /> <span>Leadership ({{ activeExecutives().length }})</span> </button> <button ui-button [variant]="activeTab() === 'press' ? 'default' : 'ghost'" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="activeTab.set('press')" > <lucide-icon [img]="Mail" class="size-3.5 shrink-0" /> <span>Press Contact</span> </button> </div> } <!-- SECTION 1: Official Logo Downloads --> @if (activeTab() === 'all' || activeTab() === 'logos') { <div class="mt-12 space-y-6"> <div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-end"> <div> <div class="text-primary flex items-center gap-2 text-xs font-semibold tracking-wider uppercase"> <lucide-icon [img]="FileImage" class="size-4 shrink-0" /> <span>Official Vectors & Assets</span> </div> <h2 class="mt-1 text-2xl font-bold tracking-tight sm:text-3xl">Official Logo Downloads</h2> <p class="text-muted-foreground mt-1 max-w-2xl text-sm"> High-resolution vector assets for print and digital publishing. Please preserve minimum clear space. </p> </div> <span ui-badge variant="outline" class="gap-1 self-start font-mono text-xs sm:self-auto"> <span>4 Approved Variants</span> </span> </div> <div class="grid gap-6 md:grid-cols-2"> @for (logo of activeLogos(); track logo.id) { <div ui-card class="border-border hover:border-border/80 flex flex-col justify-between overflow-hidden border shadow-xs transition-colors" > <div> <div [class]="logoPreviewClass(logo)"> {{ logoPreviewLabel(logo) }} <div class="absolute right-2.5 bottom-2.5"> <span class="bg-background/80 text-foreground border-border/50 rounded border px-2 py-0.5 font-mono text-xs backdrop-blur-sm" > {{ logo.dimensions }} </span> </div> </div> <div ui-card-header class="p-5 pb-3"> <div class="flex flex-wrap items-center justify-between gap-2"> <h3 ui-card-title class="text-base font-semibold">{{ logo.title }}</h3> <span ui-badge variant="secondary" class="text-xs">{{ logo.category }}</span> </div> <p ui-card-description class="mt-1 text-xs leading-relaxed"> {{ logo.description }} </p> <div class="text-muted-foreground flex items-center gap-1.5 pt-1 text-xs"> <lucide-icon [img]="Info" class="text-primary size-3.5 shrink-0" /> <span class="line-clamp-1" ><strong class="text-foreground font-medium">Best for:</strong> {{ logo.bestFor }}</span > </div> </div> </div> <div ui-card-footer class="p-5 pt-0"> <div class="w-full space-y-2"> <div class="text-muted-foreground flex items-center justify-between text-xs font-medium"> <span>Available Formats</span> <span class="font-mono text-xs">Vector & Bitmaps</span> </div> <div class="grid grid-cols-3 gap-2"> @for (fmt of logo.formats; track fmt.ext) { <button ui-button aria-label="Download logo file" variant="outline" size="sm" class="focus-visible:ring-ring h-auto min-h-8 w-full flex-col gap-0.5 px-1.5 py-2 text-center text-xs whitespace-normal focus-visible:ring-2" (click)="handleDownload(logo.id + '-' + fmt.ext)" > <div class="flex items-center gap-1 font-medium"> @if (downloadedKey() === logo.id + '-' + fmt.ext) { <lucide-icon [img]="Check" class="text-primary size-3 shrink-0" /> } @else { <lucide-icon [img]="Download" class="text-muted-foreground size-3 shrink-0" /> } <span class="truncate">{{ fmt.label.split(' ')[0] }}</span> </div> <span class="text-muted-foreground font-mono text-xs">{{ fmt.size }}</span> </button> } </div> </div> </div> </div> } </div> </div> } <!-- SECTION 2: Official Brand Palette & OKLCH Color Swatches --> @if (activeTab() === 'all' || activeTab() === 'colors') { <div class="mt-16 space-y-6"> <div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-end"> <div> <div class="text-primary flex items-center gap-2 text-xs font-semibold tracking-wider uppercase"> <lucide-icon [img]="Palette" class="size-4 shrink-0" /> <span>Token Palette & Swatches</span> </div> <h2 class="mt-1 text-2xl font-bold tracking-tight sm:text-3xl">Official Brand Palette & OKLCH</h2> <p class="text-muted-foreground mt-1 max-w-2xl text-sm"> Perceptually uniform color tokens with high WCAG contrast ratings for both dark and light UI surfaces. </p> </div> <span ui-badge variant="outline" class="gap-1 self-start font-mono text-xs sm:self-auto"> <span>5 Core Swatches</span> </span> </div> <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-5"> @for (color of activeColors(); track color.id) { <div ui-card class="border-border flex flex-col justify-between overflow-hidden border shadow-xs"> <div> <div [class]="swatchClass(color)"> <div class="flex items-center justify-between"> <span [class]="contrastPillClass(color)"> {{ color.contrast }} </span> </div> <div class="flex items-center justify-between"> <span [class]=" color.textDark ? 'font-mono text-xs font-bold text-black' : 'font-mono text-xs font-bold text-white' " > {{ color.hex }} </span> </div> </div> <div class="space-y-3 p-4"> <div> <h3 class="text-foreground text-sm font-semibold">{{ color.name }}</h3> <p class="text-primary mt-0.5 text-xs font-medium">{{ color.role }}</p> </div> <p class="text-muted-foreground text-xs leading-relaxed"> {{ color.usage }} </p> <div class="space-y-1.5 pt-1"> <div class="bg-muted/60 border-border/40 flex items-center justify-between rounded-md border px-2.5 py-1.5 text-xs" > <span class="text-muted-foreground truncate font-mono text-xs">{{ color.hex }}</span> <button type="button" class="text-foreground hover:text-primary focus-visible:ring-ring flex min-h-6 items-center gap-1 rounded px-1 text-xs transition-colors focus-visible:ring-2" [attr.aria-label]="'Copy ' + color.name + ' HEX'" (click)="copyToClipboard('hex-' + color.id, color.hex)" > @if (copiedKey() === 'hex-' + color.id) { <lucide-icon [img]="Check" class="text-primary size-3 shrink-0" /> } @else { <lucide-icon [img]="Copy" class="size-3 shrink-0" /> } <span class="text-xs">{{ copiedKey() === 'hex-' + color.id ? 'Copied' : 'HEX' }}</span> </button> </div> <div class="bg-muted/60 border-border/40 flex items-center justify-between rounded-md border px-2.5 py-1.5 text-xs" > <span class="text-muted-foreground truncate font-mono text-xs" [attr.title]="color.oklch"> {{ oklchInner(color.oklch) }} </span> <button type="button" class="text-foreground hover:text-primary focus-visible:ring-ring flex min-h-6 items-center gap-1 rounded px-1 text-xs transition-colors focus-visible:ring-2" [attr.aria-label]="'Copy ' + color.name + ' OKLCH'" (click)="copyToClipboard('oklch-' + color.id, color.oklch)" > @if (copiedKey() === 'oklch-' + color.id) { <lucide-icon [img]="Check" class="text-primary size-3 shrink-0" /> } @else { <lucide-icon [img]="Copy" class="size-3 shrink-0" /> } <span class="text-xs">{{ copiedKey() === 'oklch-' + color.id ? 'Copied' : 'OKLCH' }}</span> </button> </div> </div> </div> </div> </div> } </div> </div> } <!-- SECTION 3: Executive Leadership Headshots & Bios --> @if (activeTab() === 'all' || activeTab() === 'leadership') { <div class="mt-16 space-y-6"> <div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-end"> <div> <div class="text-primary flex items-center gap-2 text-xs font-semibold tracking-wider uppercase"> <lucide-icon [img]="User" class="size-4 shrink-0" /> <span>Media Spokespersons</span> </div> <h2 class="mt-1 text-2xl font-bold tracking-tight sm:text-3xl">Executive Leadership & Bios</h2> <p class="text-muted-foreground mt-1 max-w-2xl text-sm"> Official executive headshots and media blurbs for press releases, podcasts, and event keynotes. </p> </div> <span ui-badge variant="outline" class="gap-1 self-start font-mono text-xs sm:self-auto"> <span>300 DPI Studio Photography</span> </span> </div> <div class="grid gap-6 md:grid-cols-2"> @for (exec of activeExecutives(); track exec.id) { <div ui-card class="border-border flex flex-col justify-between overflow-hidden border shadow-xs"> <div class="space-y-5 p-6"> <div class="flex flex-col items-start gap-4 sm:flex-row sm:items-center"> <ui-avatar class="border-border size-20 shrink-0 rounded-2xl border-2 shadow-sm"> <ui-avatar-image [src]="exec.avatarUrl" [alt]="exec.name" class="object-cover" /> <ui-avatar-fallback class="bg-primary/10 text-primary rounded-2xl text-lg font-bold"> {{ exec.initials }} </ui-avatar-fallback> </ui-avatar> <div class="space-y-1"> <div class="flex flex-wrap items-center gap-2"> <h3 class="text-foreground text-lg font-bold">{{ exec.name }}</h3> <span ui-badge variant="secondary" class="text-xs font-normal">Executive</span> </div> <p class="text-primary text-xs font-medium">{{ exec.role }}</p> <p class="text-muted-foreground font-mono text-xs">{{ exec.dimensions }}</p> </div> </div> <p class="text-muted-foreground text-xs leading-relaxed"> {{ exec.bio }} </p> <div class="space-y-1.5"> <span class="text-foreground text-xs font-medium">Speaking Topics & Focus Areas:</span> <div class="flex flex-wrap gap-1.5"> @for (topic of exec.topics; track topic) { <span ui-badge variant="outline" class="bg-muted/40 text-xs font-normal"> {{ topic }} </span> } </div> </div> </div> <div ui-card-footer class="border-border/40 bg-muted/20 mt-2 flex flex-col items-stretch justify-between gap-3 border-t p-6 pt-0 sm:flex-row sm:items-center" > <div class="text-muted-foreground flex items-center gap-1 text-xs"> <lucide-icon [img]="FileImage" class="text-primary size-3.5 shrink-0" /> <span>{{ exec.fileSize }} RAW TIFF & JPG</span> </div> <button ui-button aria-label="Download executive headshot" size="sm" variant="default" class="focus-visible:ring-ring h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal focus-visible:ring-2" (click)="handleDownload('headshot-' + exec.id)" > @if (downloadedKey() === 'headshot-' + exec.id) { <lucide-icon [img]="Check" class="size-3.5 shrink-0" /> } @else { <lucide-icon [img]="Download" class="size-3.5 shrink-0" /> } <span>{{ downloadedKey() === 'headshot-' + exec.id ? 'Headshot Downloaded' : 'Download Headshot (300 DPI)' }}</span> </button> </div> </div> } </div> </div> } <!-- SECTION 4: Press Hub & Media Contact + Brand Guidelines --> @if (activeTab() === 'all' || activeTab() === 'press') { <div class="mt-16 grid gap-6 lg:grid-cols-2"> <div ui-card class="border-border flex flex-col justify-between border shadow-xs"> <div ui-card-header class="p-6 pb-4"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="text-primary flex items-center gap-2 text-xs font-semibold tracking-wider uppercase"> <lucide-icon [img]="Mail" class="size-4 shrink-0" /> <span>Press & Media Inquiries</span> </div> <span ui-badge variant="outline" class="font-mono text-xs">{{ version }}</span> </div> <h3 ui-card-title class="mt-1 text-xl font-bold">Get in Touch with Media Relations</h3> <p ui-card-description class="text-xs leading-relaxed"> Direct access for accredited tech journalists, conference organizers, analysts, and podcast hosts. </p> </div> <div ui-card-content class="space-y-4 p-6 pt-0"> <div class="border-border bg-muted/40 flex flex-wrap items-center justify-between gap-3 rounded-lg border p-3.5" > <div class="flex min-w-0 items-center gap-3"> <div class="bg-primary/10 text-primary flex size-9 shrink-0 items-center justify-center rounded-md"> <lucide-icon [img]="Mail" class="size-4 shrink-0" /> </div> <div> <p class="text-foreground text-xs font-semibold">{{ pressEmail }}</p> <p class="text-muted-foreground text-xs">Official Media & Inquiries Desk</p> </div> </div> <button ui-button variant="outline" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="copyToClipboard('press-email', pressEmail)" > @if (copiedKey() === 'press-email') { <lucide-icon [img]="Check" class="text-primary size-3.5 shrink-0" /> } @else { <lucide-icon [img]="Copy" class="size-3.5 shrink-0" /> } <span>{{ copiedKey() === 'press-email' ? 'Copied' : 'Copy' }}</span> </button> </div> <div class="grid grid-cols-2 gap-3 pt-1"> <div class="border-border/60 bg-card space-y-1 rounded-lg border p-3"> <div class="text-foreground flex items-center gap-1.5 text-xs font-semibold"> <lucide-icon [img]="ShieldCheck" class="text-primary size-3.5 shrink-0" /> <span>< 24h Response</span> </div> <p class="text-muted-foreground text-xs"> Prompt turnaround on verified interview and quote requests. </p> </div> <div class="border-border/60 bg-card space-y-1 rounded-lg border p-3"> <div class="text-foreground flex items-center gap-1.5 text-xs font-semibold"> <lucide-icon [img]="Globe" class="text-primary size-3.5 shrink-0" /> <span>Embargo Previews</span> </div> <p class="text-muted-foreground text-xs"> Advance beta access and documentation under NDA/embargo. </p> </div> </div> </div> <div ui-card-footer class="border-border/40 bg-muted/10 mt-2 border-t p-6 pt-0"> <div class="flex w-full flex-col items-stretch justify-between gap-3 pt-3 sm:flex-row sm:items-center"> <span class="text-muted-foreground text-xs">Need company numbers & metrics?</span> <button ui-button aria-label="Download company fact sheet" variant="outline" size="sm" class="h-auto min-h-8 gap-1.5 text-center text-xs whitespace-normal" (click)="handleDownload('factsheet-pdf')" > @if (downloadedKey() === 'factsheet-pdf') { <lucide-icon [img]="Check" class="text-primary size-3.5 shrink-0" /> } @else { <lucide-icon [img]="FileText" class="size-3.5 shrink-0" /> } <span>{{ downloadedKey() === 'factsheet-pdf' ? 'Downloaded' : 'Company Fact Sheet (.PDF · ' + factSheetPdfSize + ')' }}</span> </button> </div> </div> </div> <div ui-card class="border-border flex flex-col justify-between border shadow-xs"> <div ui-card-header class="p-6 pb-4"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="text-primary flex items-center gap-2 text-xs font-semibold tracking-wider uppercase"> <lucide-icon [img]="FileCode" class="size-4 shrink-0" /> <span>Brand Rules & Boilerplate</span> </div> <button ui-button variant="ghost" size="xs" class="text-primary h-auto min-h-8 gap-1 text-center text-xs whitespace-normal" (click)="copyToClipboard('boilerplate-text', boilerplateText)" > @if (copiedKey() === 'boilerplate-text') { <lucide-icon [img]="Check" class="size-3 shrink-0" /> } @else { <lucide-icon [img]="Copy" class="size-3 shrink-0" /> } <span>{{ copiedKey() === 'boilerplate-text' ? 'Copied Boilerplate' : 'Copy Boilerplate' }}</span> </button> </div> <h3 ui-card-title class="mt-1 text-xl font-bold">Official Company Boilerplate</h3> <p ui-card-description class="text-xs leading-relaxed"> Approved copy for standard "About the Company" sections in press releases and event booklets. </p> </div> <div ui-card-content class="space-y-4 p-6 pt-0"> <div class="border-border bg-muted/40 text-muted-foreground rounded-lg border p-3.5 text-xs leading-relaxed italic" > "{{ boilerplateText }}" </div> <div class="space-y-2"> <span class="text-foreground text-xs font-semibold">Brand Usage Rules:</span> <div class="grid gap-2"> @for (rule of visibleGuidelines(); track rule.text) { <div class="text-muted-foreground flex items-start gap-2 text-xs"> @if (rule.type === 'do') { <lucide-icon [img]="CheckCircle2" class="text-success mt-0.5 size-3.5 shrink-0" /> } @else { <lucide-icon [img]="XCircle" class="text-warning mt-0.5 size-3.5 shrink-0" /> } <span class="leading-normal">{{ rule.text }}</span> </div> } </div> </div> </div> <div ui-card-footer class="border-border/40 bg-muted/10 mt-2 border-t p-6 pt-0"> <div class="text-muted-foreground flex w-full items-center justify-between pt-3 text-xs"> <span>Questions regarding brand trademark usage?</span> <a [href]="'mailto:' + pressEmail + '?subject=Brand%20Guideline%20Inquiry'" class="text-primary inline-flex items-center gap-1 font-medium hover:underline" > Contact Legal Desk <lucide-icon [img]="ExternalLink" class="size-3 shrink-0" /> </a> </div> </div> </div> </div> } </div> </div> `, }) export class UiPressKitBrandAssetsComponent { protected readonly Check = Check protected readonly CheckCircle2 = CheckCircle2 protected readonly Copy = Copy protected readonly Download = Download protected readonly ExternalLink = ExternalLink protected readonly FileArchive = FileArchive protected readonly FileCode = FileCode protected readonly FileImage = FileImage protected readonly FileText = FileText protected readonly Globe = Globe protected readonly Info = Info protected readonly Layers = Layers protected readonly Mail = Mail protected readonly Palette = Palette protected readonly ShieldCheck = ShieldCheck protected readonly User = User protected readonly XCircle = XCircle @Input() companyName = 'UIPKGE' @Input() title = 'Brand Guidelines & Press Kit' @Input() subtitle = 'Official logos, brand colors, typography rules, and executive media assets.' @Input() version = 'v2.4' @Input() lastUpdated = 'August 2026' @Input() pressEmail = '[email protected]' @Input() pressKitZipSize = '48 MB' @Input() pressKitZipUrl = '#' @Input() factSheetPdfSize = '2.4 MB' @Input() boilerplateText = 'UIPKGE is an open-source multi-framework UI registry providing accessible standalone components for Vue, React, and Angular with 100% code ownership. Headquartered in San Francisco with a distributed core maintainer team.' @Input() variant: 'default' | 'compact' = 'default' @Input('class') className?: string private readonly logosInput = signal<LogoAsset[] | undefined>(undefined) private readonly colorsInput = signal<BrandColor[] | undefined>(undefined) private readonly executivesInput = signal<Executive[] | undefined>(undefined) private readonly guidelinesInput = signal<BrandGuidelineRule[] | undefined>(undefined) @Input() set logos(v: LogoAsset[] | undefined) { this.logosInput.set(v) } @Input() set colors(v: BrandColor[] | undefined) { this.colorsInput.set(v) } @Input() set executives(v: Executive[] | undefined) { this.executivesInput.set(v) } @Input() set guidelines(v: BrandGuidelineRule[] | undefined) { this.guidelinesInput.set(v) } readonly activeLogos = computed(() => this.logosInput() ?? DEFAULT_LOGOS) readonly activeColors = computed(() => this.colorsInput() ?? DEFAULT_COLORS) readonly activeExecutives = computed(() => this.executivesInput() ?? DEFAULT_EXECUTIVES) readonly activeGuidelines = computed(() => this.guidelinesInput() ?? DEFAULT_GUIDELINES) readonly visibleGuidelines = computed(() => this.activeGuidelines().slice(0, 4)) readonly activeTab = signal<PressKitTab>('all') readonly copiedKey = signal<string | null>(null) readonly downloadedKey = signal<string | null>(null) private copyTimer: ReturnType<typeof setTimeout> | undefined private downloadTimer: ReturnType<typeof setTimeout> | undefined get rootClass(): string { return cn('bg-background text-foreground w-full transition-colors', this.className) } logoPreviewClass(logo: LogoAsset): string { return cn( 'relative flex min-h-[190px] items-center justify-center overflow-hidden p-4 text-center transition-colors sm:p-8', logo.theme === 'dark' ? 'bg-zinc-950 text-white' : logo.theme === 'light' && logo.id === 'monochrome-light' ? 'bg-zinc-100 text-zinc-950 dark:bg-zinc-200' : 'bg-muted/40 text-foreground bg-[radial-gradient(var(--border)_1px,transparent_1px)] [background-size:12px_12px]', ) } /** Short wordmark preview label (the Vue block renders inline SVGs per logo id). */ logoPreviewLabel(logo: LogoAsset): string { if (logo.id === 'app-icon-mark') return '◈' return `${this.companyName} .dev` } swatchClass(color: BrandColor): string { return cn('border-border/40 relative flex h-24 w-full flex-col justify-between border-b p-3', color.bgClass) } contrastPillClass(color: BrandColor): string { return cn( 'rounded-full px-2 py-0.5 text-xs font-medium backdrop-blur-md', color.textDark ? 'bg-black/10 text-black' : 'bg-white/20 text-white', ) } oklchInner(oklch: string): string { const open = oklch.indexOf('(') const close = oklch.lastIndexOf(')') if (open >= 0 && close > open) return oklch.slice(open + 1, close) return oklch } async copyToClipboard(key: string, text: string): Promise<void> { try { await navigator.clipboard.writeText(text) } catch { // Clipboard unavailable (jsdom) — still show feedback. } this.copiedKey.set(key) clearTimeout(this.copyTimer) this.copyTimer = setTimeout(() => { if (this.copiedKey() === key) this.copiedKey.set(null) }, 1800) } handleDownload(key: string): void { this.downloadedKey.set(key) clearTimeout(this.downloadTimer) this.downloadTimer = setTimeout(() => { if (this.downloadedKey() === key) this.downloadedKey.set(null) }, 2000) } }src/app/components/blocks/press-kit-brand-assets/index.ts0.2 kB
export { UiPressKitBrandAssetsComponent, type BrandColor, type BrandGuidelineRule, type Executive, type LogoAsset, } from './press-kit-brand-assets.component'
Raw manifest:https://uipkge.dev/r/angular/press-kit-brand-assets.json