Files
Voiced/src/styles/carbon.scss
T
xavierk e1edf02448 Add Rust logo pipeline: trim, metrics, knockout and Branding settings
Logos (PNG, JPEG, WebP) are decoded, trimmed to their ink box, downscaled
to 1200 px and re-encoded as a content-addressed print PNG plus a white
knockout variant when the logo has alpha; aspect, ink density, mean colour
and kind (wordmark, mark, tall) are stored with the settings and frozen in
the issue snapshot. Legacy logos are derived at startup. The Test Vendor logo
PDF shrinks from 334 KB to 92 KB. Settings gains a Branding section with
white and dark previews and a business-name toggle (migration M5).
2026-10-04 06:31:30 +05:30

455 lines
8.4 KiB
SCSS

// Voiced — Carbon Design System, compiled with self-hosted IBM Plex for offline use.
// Font-face emission from Carbon is disabled so we can serve the fonts ourselves
// instead of pulling them from IBM's CDN (the app must work with no network).
@use '@carbon/react' with (
$css--font-face: false,
$use-akamai-cdn: false
);
/* ---------- IBM Plex (bundled in /public/fonts) ---------- */
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('/fonts/IBMPlexSans-Light.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 300;
font-display: swap;
src: url('/fonts/IBMPlexSans-LightItalic.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/IBMPlexSans-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('/fonts/IBMPlexSans-Italic.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/fonts/IBMPlexSans-Medium.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 500;
font-display: swap;
src: url('/fonts/IBMPlexSans-MediumItalic.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/IBMPlexSans-SemiBold.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 600;
font-display: swap;
src: url('/fonts/IBMPlexSans-SemiBoldItalic.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('/fonts/IBMPlexSans-Bold.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Sans';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url('/fonts/IBMPlexSans-BoldItalic.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/fonts/IBMPlexMono-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('/fonts/IBMPlexMono-Medium.woff2') format('woff2');
}
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('/fonts/IBMPlexMono-SemiBold.woff2') format('woff2');
}
/* ---------- App shell ---------- */
html,
body,
#root {
height: 100%;
}
body {
margin: 0;
background-color: var(--cds-background);
color: var(--cds-text-primary);
}
.voiced-brand {
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 0;
}
.voiced-brand__logo {
height: 1.25rem;
width: auto;
display: block;
}
.voiced-brand__divider {
width: 1px;
height: 1.25rem;
background: var(--cds-border-subtle-01);
}
.voiced-brand__product {
font-weight: 600;
letter-spacing: 0.02em;
white-space: nowrap;
}
.voiced-header-action {
display: flex;
align-items: center;
gap: 0.5rem;
}
// Views stay mounted while hidden so their state survives navigation.
[hidden] {
display: none !important;
}
.voiced-page {
padding: 2rem;
max-width: 1600px;
margin: 0 auto;
width: 100%;
box-sizing: border-box;
}
.voiced-page__title {
margin-bottom: 0.25rem;
}
.voiced-page__subtitle {
color: var(--cds-text-secondary);
margin-bottom: 2rem;
}
.voiced-stack > * + * {
margin-top: 1.5rem;
}
.voiced-row-gap {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: flex-end;
}
.voiced-muted {
color: var(--cds-text-secondary);
}
.voiced-empty {
padding: 3rem 1rem;
text-align: center;
color: var(--cds-text-secondary);
}
/* ---------- PDF preview column ---------- */
.voiced-page-setup {
margin-bottom: 1rem;
}
.voiced-page-setup__row {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 1rem;
}
.voiced-page-setup__field {
min-width: 11rem;
flex: 1 1 11rem;
}
.voiced-page-setup__field .cds--label {
display: block;
margin-bottom: 0.5rem;
}
.voiced-page-setup__footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem 1rem;
margin-top: 0.75rem;
}
.voiced-page-setup__note {
font-size: 0.75rem;
flex: 1 1 12rem;
}
.voiced-pdf-preview {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 0;
}
.voiced-pdf-preview__status {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.25rem 1rem;
min-height: 2rem;
}
.voiced-pdf-preview__chips,
.voiced-pdf-preview__actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.voiced-pdf-preview__empty {
display: flex;
align-items: center;
justify-content: center;
height: 12rem;
background: var(--cds-layer-accent);
color: var(--cds-text-secondary);
}
.voiced-pdf-viewer {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 0;
}
.voiced-pdf-viewer__bar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.5rem 1rem;
}
.voiced-pdf-viewer__zoom {
flex: 0 1 18rem;
}
.voiced-pdf-viewer__page {
font-size: 0.875rem;
color: var(--cds-text-secondary);
white-space: nowrap;
}
.voiced-pdf-preview__scroll {
height: calc(100vh - 25rem);
min-height: 20rem;
overflow: auto;
background: var(--cds-layer-accent);
}
.voiced-pdf-viewer--focus .voiced-pdf-preview__scroll {
height: calc(100vh - 12rem);
}
/* The wrapper is as wide as the widest page so a zoomed page scrolls instead of being clipped. */
.voiced-pdf-preview__pages {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
min-width: 100%;
width: max-content;
padding: 16px;
}
.voiced-pdf-preview__page {
flex: none;
background: #ffffff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}
.voiced-pdf-preview__canvas {
display: block;
width: 100%;
height: 100%;
}
.voiced-pdf-focus .cds--modal-container {
width: 96vw;
max-width: 96vw;
height: 94vh;
max-height: 94vh;
}
.voiced-pdf-issues {
width: 22rem;
max-width: 80vw;
max-height: 20rem;
overflow: auto;
padding: 0.75rem 1rem;
}
.voiced-pdf-issues__list {
list-style: none;
margin: 0;
padding: 0;
}
.voiced-pdf-issues__item + .voiced-pdf-issues__item {
margin-top: 0.75rem;
}
.voiced-pdf-issues__head {
display: flex;
align-items: center;
gap: 0.5rem;
}
.voiced-pdf-issues__message {
margin: 0.25rem 0 0;
font-size: 0.875rem;
}
.voiced-tag--warn.cds--tag {
background-color: var(--cds-support-warning);
color: #161616;
}
/* ---------- export feedback ---------- */
.voiced-notice-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
}
.voiced-export-split {
display: inline-flex;
align-items: stretch;
}
.voiced-diagnostics__check {
padding: 0.5rem 0;
border-bottom: 1px solid var(--cds-border-subtle-01);
}
.voiced-diagnostics__head {
display: flex;
align-items: center;
gap: 0.5rem;
}
.voiced-diagnostics__label {
flex: 1 1 auto;
min-width: 0;
}
.voiced-diagnostics__ms {
color: var(--cds-text-secondary);
font-size: 0.75rem;
white-space: nowrap;
}
.voiced-diagnostics__detail {
margin: 0.25rem 0 0;
color: var(--cds-text-secondary);
font-size: 0.75rem;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
/* ---------- Logo preview (Settings -> Branding) ---------- */
.voiced-logo-preview {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.voiced-logo-preview__item {
margin: 0;
width: 14rem;
}
.voiced-logo-preview__surface {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
height: 6rem;
padding: 1rem;
border: 1px solid var(--cds-border-subtle-01);
overflow: hidden;
img {
display: block;
max-width: 100%;
max-height: 4rem;
object-fit: contain;
}
}
.voiced-logo-preview__surface--light {
background: #ffffff;
}
.voiced-logo-preview__surface--dark {
background: #161616;
}
.voiced-logo-preview__chip {
display: flex;
box-sizing: border-box;
max-width: 100%;
padding: 0.5rem 0.75rem;
background: #ffffff;
border-radius: 0.5rem;
img {
max-height: 3rem;
}
}
.voiced-logo-preview__item figcaption {
margin-top: 0.5rem;
font-size: 0.75rem;
}