Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/frontend/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion apps/frontend/src/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
useCommandMenuShortcut,
} from "@/components/CommandMenu";
import { BrandMark } from "@/components/BrandMark";
import { BrandWordmark } from "@/components/BrandWordmark";
import { DemoBanner } from "@/components/DemoBanner";
import { HeaderBell } from "@/components/HeaderBell";
import { LanguageToggle } from "@/components/LanguageToggle";
Expand Down Expand Up @@ -214,7 +215,7 @@ function SidebarNav({
) : (
<span className="flex items-center gap-2">
<BrandMark size={20} />
{t("app.name")}
<BrandWordmark />
</span>
)}
</div>
Expand Down
31 changes: 31 additions & 0 deletions apps/frontend/src/components/BrandLockup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* BrandLockup — the full TRUSCA logo: mark + "TRUSCA" wordmark + the
* "TrustedOSS SCA" tagline (the SCA tool of the TrustedOSS initiative).
*
* Used where there is vertical room (the auth gateway, brand showcase).
* Tight surfaces — the 48 px sidebar / header — use the reduced lockup
* (BrandMark + BrandWordmark, no tagline). See docs/brand-trusca.md.
*
* The tagline is a brand string (not translated) and is NOT uppercased —
* the umbrella name "TrustedOSS" keeps its camel casing. Its colour uses the
* theme's muted-foreground token (passes WCAG AA), while the mark gradient
* and the teal wordmark are fixed brand colours.
*/
import { BrandMark } from "@/components/BrandMark";
import { BrandWordmark } from "@/components/BrandWordmark";

export function BrandLockup() {
return (
<div className="flex items-center gap-3.5">
<BrandMark size={48} />
<div className="flex flex-col justify-center">
<span className="text-3xl font-extrabold leading-none tracking-tight">
<BrandWordmark />
</span>
<span className="mt-1.5 text-xs font-semibold leading-none tracking-wide text-muted-foreground">
TrustedOSS SCA
</span>
</div>
</div>
);
}
22 changes: 16 additions & 6 deletions apps/frontend/src/components/BrandMark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
* check), picked in the W1 rebrand (see docs/brand-trusca.md).
*
* Canonical in-app rendering of the mark (the same geometry ships as
* public/favicon.svg and the docs-site logo). Used by the AppShell collapsed
* rail; reuse this component anywhere the symbol is needed instead of
* re-inlining the paths.
* public/favicon.svg and the docs-site logo). Reuse this component anywhere
* the symbol is needed instead of re-inlining the paths.
*
* Palette is fixed brand ink/paper (not theme tokens) so the tile reads
* identically on any surface, matching the favicon.
* Palette is fixed brand colour (not theme tokens) so the tile reads
* identically on any surface: a teal gradient tile (#2dd4bf → #0f766e) with
* the hexagon + check in paper (#fafafa). Teal is the TRUSCA brand colour;
* the wordmark (BrandWordmark) uses the same teal. The gradient id is
* per-instance (useId) so multiple marks on one page never collide.
*/
import { useId } from "react";

export function BrandMark({ size = 24 }: { size?: number }) {
const gradId = useId();
return (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -22,7 +26,13 @@ export function BrandMark({ size = 24 }: { size?: number }) {
aria-hidden
focusable="false"
>
<rect width="32" height="32" rx="7" fill="#18181b" />
<defs>
<linearGradient id={gradId} x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stopColor="#2dd4bf" />
<stop offset="1" stopColor="#0f766e" />
</linearGradient>
</defs>
<rect width="32" height="32" rx="8" fill={`url(#${gradId})`} />
<path
d="M16 6.5 L24.2 11.25 V20.75 L16 25.5 L7.8 20.75 V11.25 Z"
fill="none"
Expand Down
14 changes: 14 additions & 0 deletions apps/frontend/src/components/BrandWordmark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* BrandWordmark — the "TRUSCA" lettering in a single brand-teal colour
* (#0f766e), matching the mark tile. Weight / size inherit from the
* surrounding context (e.g. the sidebar header is semibold-sm; the auth
* lockup sizes it up). See docs/brand-trusca.md and BrandLockup.
*
* "TRUSCA" is the product name, identical in every locale, so it is not
* translated. Colour is a fixed hex (not a theme token) to match the mark,
* which renders identically on any surface.
*/

export function BrandWordmark() {
return <span style={{ color: "#0f766e" }}>TRUSCA</span>;
}
16 changes: 8 additions & 8 deletions apps/frontend/src/pages/auth/AuthLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";

import { BrandLockup } from "@/components/BrandLockup";
import { DemoBanner } from "@/components/DemoBanner";
import { LanguageToggle } from "@/components/LanguageToggle";
import {
Expand Down Expand Up @@ -32,27 +32,27 @@ export function AuthLayout({
footer,
testId,
}: AuthLayoutProps) {
const { t } = useTranslation();
return (
<div className="min-h-screen bg-background text-foreground">
{/* B5 — surface read-only demo mode to unauthenticated visitors too, not
just inside the post-login shell. Self-gated: renders nothing on a
normal deploy. */}
<DemoBanner />
<header
className="flex items-center justify-between border-b px-6"
className="flex items-center justify-end border-b px-6"
style={{ height: "var(--layout-header)" }}
>
<span className="text-sm font-semibold tracking-tight">
{t("app.name")}
</span>
<LanguageToggle />
</header>
<main
className="mx-auto flex w-full max-w-md flex-col gap-6 px-6 py-12"
className="mx-auto flex w-full max-w-md flex-col items-center gap-6 px-6 py-12"
data-testid={testId}
>
<Card>
{/* Full brand logo (mark + wordmark + tagline) — the gateway is the
one surface with room for the tagline; the post-login sidebar uses
the compact mark + wordmark. */}
<BrandLockup />
<Card className="w-full">
<CardHeader>
<CardTitle>{title}</CardTitle>
{subtitle ? <CardDescription>{subtitle}</CardDescription> : null}
Expand Down
3 changes: 2 additions & 1 deletion docs-site/static/img/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion docs-site/static/img/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 18 additions & 6 deletions docs/brand-trusca.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,24 @@
- **C Stacked SBOM** — 구성요소 목록 막대 + 최상단 검증
- **선정안**: **A Hex Check** (2026-06-12 확정) — 패키지 육각 + 검증 체크.
16px 가독성이 가장 좋고 보안 도구 관례에 부합.
- **팔레트**: 기존 토큰만 — ink `#18181b`(warm near-black), paper `#fafafa`,
accent `#2563eb`(README 배지에 이미 쓰는 블루). 새 브랜드 컬러를 만들지 않는다.
- **워드마크**: Inter semibold tracking-tight, `TRU`(ink) + `SCA`(accent).
- **적용 자산**(선정 후 교체): `docs-site/static/img/{logo,favicon}.svg`,
`apps/frontend/public/favicon.svg`(+ `index.html` link), AppShell 접힘 레일
모노그램.
- **팔레트**(2026-06-13 갱신): 브랜드 컬러 = **틸**, paper `#fafafa`, ink `#18181b`.
단색 near-black 타일이 "너무 검다"는 피드백 + BomLens 레퍼런스(그라데이션 타일·
단색 굵은 워드마크·태그라인) 검토를 거쳐, **마크 타일은 틸 그라데이션
`#2dd4bf → #0f766e`**(대각, top-left→bottom-right)로, 육각+체크는 paper로 칠한다.
(기존 계획의 블루 `#2563eb`에서 틸로 변경, 리스크 Low의 블루와도 분리.)
틸 `#0f766e`는 흰 배경 5.47:1로 WCAG AA 통과.
- **워드마크**: Inter, 굵게(extrabold), tracking-tight, **"TRUSCA" 단색 틸 `#0f766e`**.
단어 중간 색 분리(구 `TRU`+`SCA`)는 폐기 — 싸구려로 읽혀 BomLens식 단색으로 통일.
- **태그라인**: "TrustedOSS SCA"(= TrustedOSS 이니셔티브의 SCA 도구). muted gray,
대문자 변환 안 함(우산명 "TrustedOSS" 카멜표기 보존). 단어 색 분리 대신 이
태그라인이 SCA·우산 관계를 담당.
- **락업**: 풀 락업(마크+워드마크+태그라인)은 여유 있는 곳(로그인 게이트웨이·브랜드
쇼케이스)에 — `apps/frontend/src/components/BrandLockup.tsx`. 좁은 48px
사이드바/헤더는 축약 락업(마크+워드마크, 태그라인 생략).
구현: `BrandMark.tsx`(그라데이션 타일)·`BrandWordmark.tsx`(틸 워드마크).
로그인 카드 타이틀 등 문장 속 "TRUSCA"는 평문 유지(로케일별 어순 상이).
- **적용 자산**: 타일 틸 = `apps/frontend/src/components/BrandMark.tsx`,
`apps/frontend/public/favicon.svg`, `docs-site/static/img/{logo,favicon}.svg`.

## 5. 보존 식별자 (의도적 비변경)

Expand Down
Loading