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
187 changes: 174 additions & 13 deletions apps/web/app/layouts/auth.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,177 @@
<template>
<div class="min-h-screen flex flex-col bg-canvas text-ink">
<header class="h-16 flex items-center justify-end gap-3 px-4 md:px-6">
<LocaleSwitcher />
<ClientOnly>
<ColorModeToggle />
<template #fallback>
<span class="h-9 w-9 border border-line bg-raised" aria-hidden="true" />
</template>
</ClientOnly>
</header>
<main class="flex flex-1 items-center justify-center px-4 pb-16">
<slot />
</main>
<div class="auth-shell bg-canvas text-ink">
<div class="auth-entry">
<header class="auth-toolbar flex items-center justify-end gap-3 px-5 sm:px-8">
<LocaleSwitcher />
<ClientOnly>
<ColorModeToggle />
<template #fallback>
<span class="h-11 w-11" aria-hidden="true" />
</template>
</ClientOnly>
</header>
<main class="auth-main">
<div class="auth-form">
<NuxtLink
class="auth-brand focus-ring mb-10 inline-flex items-center gap-3 text-ink"
to="/"
>
<span class="auth-mark bg-accent" aria-hidden="true" />
<span class="text-base font-650 tracking-tight">Code Zero</span>
</NuxtLink>
<slot />
</div>
</main>
</div>
<aside class="auth-story" :aria-label="$t('auth.brand.label')">
<div class="auth-story-copy">
<h2 class="m-0 font-600 tracking-tight">{{ $t('auth.brand.title') }}</h2>
<p class="mb-0 mt-6 text-base leading-relaxed">{{ $t('auth.brand.description') }}</p>
</div>
<span class="auth-version mono">v{{ buildInfo.version }}</span>
</aside>
</div>
</template>

<script setup lang="ts">
const buildInfo = useBuildInfo();

useHead({ htmlAttrs: { 'data-layout': 'auth' } });
</script>

<style scoped>
.auth-shell {
min-height: 100svh;
display: grid;
grid-template-columns: minmax(0, 1fr);
}

.auth-entry {
min-width: 0;
display: flex;
flex-direction: column;
}

.auth-toolbar {
min-height: 5rem;
}

.auth-main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem 1.5rem 5rem;
}

.auth-form {
width: 100%;
max-width: 24rem;
}

.auth-brand {
min-height: 2.75rem;
text-decoration: none;
}

.auth-mark {
width: 1.75rem;
height: 1.75rem;
mask: url('/logo-mark.svg') center / contain no-repeat;
}

.auth-form :deep(h1) {
font-size: clamp(1.5rem, 2vw, 1.875rem);
line-height: 1.25;
}

.auth-form :deep(input),
.auth-form :deep(button),
.auth-form :deep(.btn-subtle),
.auth-toolbar :deep(button),
.auth-toolbar :deep(select) {
min-height: 2.75rem;
font-size: 1rem;
}

.auth-toolbar :deep(button) {
min-width: 2.75rem;
}

.auth-form :deep(label > span) {
font-size: 0.875rem;
font-weight: 500;
letter-spacing: normal;
text-transform: none;
}

.auth-form :deep(p),
.auth-form :deep(.btn-link) {
font-size: 0.875rem;
}

.auth-form :deep(.btn-link) {
min-height: 2.75rem;
align-content: center;
}

.auth-story {
position: relative;
display: none;
min-width: 0;
padding: 4rem;
background-color: color-mix(in oklch, var(--cz-accent) 32%, var(--cz-panel));
/* The reference's checkerboard is a static brand texture, with no image or script cost. */
background-image: repeating-conic-gradient(
color-mix(in oklch, var(--cz-accent) 10%, transparent) 0% 25%,
transparent 0% 50%
);
background-size: 4rem 4rem;
}

.auth-story-copy {
max-width: 32rem;
}

.auth-story h2 {
font-size: clamp(2.25rem, 3.5vw, 3.75rem);
line-height: 1.1;
text-wrap: balance;
}

.auth-story p {
max-width: 29rem;
color: var(--cz-ink);
}

.auth-version {
position: absolute;
inset-block-end: 2.5rem;
inset-inline-start: 4rem;
color: var(--cz-ink);
}

@media (min-width: 64rem) {
.auth-shell {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.auth-story {
display: flex;
align-items: center;
}
}
</style>

<style>
/*
* The narrative panel runs to the viewport edge. The gutter `tokens.css` reserves for a scrollbar
* would leave a canvas-coloured strip beside it wherever scrollbars are not overlaid, and the
* split layout fits the viewport, so it gives the gutter back.
*/
@media (min-width: 64rem) {
html[data-layout='auth'] {
scrollbar-gutter: auto;
}
}
</style>
2 changes: 1 addition & 1 deletion apps/web/app/pages/(auth)/login.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<section class="panel w-full max-w-88 p-6">
<section class="w-full">
<h1 class="m-0 text-lg font-650 tracking-tight">{{ $t('auth.login.title') }}</h1>
<p class="mb-6 mt-1 text-xs text-muted">{{ $t('auth.login.subtitle') }}</p>

Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/pages/(auth)/signup.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<section class="panel w-full max-w-88 p-6">
<section class="w-full">
<h1 class="m-0 text-lg font-650 tracking-tight">{{ $t('auth.signup.title') }}</h1>
<p class="mb-6 mt-1 text-xs text-muted">{{ $t('auth.login.subtitle') }}</p>

Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/pages/device.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<section class="panel w-full max-w-88 p-6">
<section class="w-full">
<h1 class="m-0 text-lg font-650 tracking-tight">
{{ stage === 'confirm' ? $t('auth.device.confirmTitle') : $t('auth.device.title') }}
</h1>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/pages/invite.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<section class="panel w-full max-w-88 p-6">
<section class="w-full">
<h1 class="m-0 text-lg font-650 tracking-tight">{{ $t('auth.invite.title') }}</h1>
<p v-if="organizationName" class="mb-6 mt-1 text-xs text-muted">{{ organizationName }}</p>

Expand Down
26 changes: 26 additions & 0 deletions apps/web/public/logo-mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/i18n/locales/en/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
"EMAIL_MISMATCH": "That invitation was issued for a different email address.",
"SEAT_LIMIT_REACHED": "That organization has no seats left.",
"ORG_DISABLED": "That organization is currently disabled."
},
"brand": {
"label": "About Code Zero",
"title": "Every change, backed by evidence.",
"description": "Review pull requests, verify fixes and keep control of what your agents can change. Open source, ready to self-host."
}
}
}
5 changes: 5 additions & 0 deletions packages/i18n/locales/it/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
"EMAIL_MISMATCH": "Questo invito è stato emesso per un altro indirizzo email.",
"SEAT_LIMIT_REACHED": "Questa organizzazione non ha più posti disponibili.",
"ORG_DISABLED": "Questa organizzazione è attualmente disabilitata."
},
"brand": {
"label": "Informazioni su Code Zero",
"title": "Ogni modifica, verificata.",
"description": "Esamina le pull request, verifica le correzioni e mantieni il controllo sulle modifiche degli agenti. Open source, pronto per il self hosting."
}
}
}
15 changes: 15 additions & 0 deletions packages/i18n/schemas/auth.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@
}
},
"additionalProperties": false
},
"brand": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
Expand Down
7 changes: 6 additions & 1 deletion packages/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ pull request and say why.

- Corners are square. `rounded-full` is allowed only for status dots and avatars.
- No drop shadows and no decorative gradients. Depth comes from `canvas` → `panel` → `raised` and a
`line` border. The page grid (`--cz-grid`) and the marketing hero halo are the only gradients.
`line` border. The page grid (`--cz-grid`), marketing hero halo, and authentication checkerboard
are the registered background textures. The auth checkerboard uses the shared accent palette
to reproduce the supplied reference.

The authentication layout uses the existing Code Zero mark in a monochrome accent treatment.

## Type

Expand All @@ -26,6 +30,7 @@ pull request and say why.

## Density

- Authentication forms use 44 px controls and sentence-case 14 px labels to match the login reference.
- The dashboard is dense: 36 px controls (`h-9`), `text-xs` body in panels.
- The marketing site is comfortable: 44 px primary controls (`h-11`), `text-sm` and up.
- Shared shortcuts (`panel`, `mono`, `focus-ring`, `btn-subtle`, `btn-accent`, `btn-icon`,
Expand Down
Loading