refactor: reorganize component imports and enhance index page layout
- Grouped component imports into categories for better organization. - Updated the layout of the index page to include a gallery structure. - Added new sections for Button, Badge, Selection Controls, TextField, Select, Numeric Stepper, Avatar, Loading Bar, List Item, Link, Tabs, Breadcrumb, Pagination, Notification, Tooltip, Card, Navbar, Sidebar, Modal, and Icons. - Improved styling for better visual hierarchy and spacing. - Implemented a theme toggle feature with persistent state using localStorage.
This commit is contained in:
@@ -1,30 +1,28 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.link {
|
||||
.link__container {
|
||||
width: fit-content;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.link {
|
||||
text-decoration: none;
|
||||
color: var(--nds-primary);
|
||||
@include text-base;
|
||||
&:hover {
|
||||
color: var(--nds-text);
|
||||
opacity: 0.25;
|
||||
}
|
||||
&:active {
|
||||
color: var(--nds-text);
|
||||
opacity: 0.25;
|
||||
}
|
||||
&__container {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: var(--nds-border-width-medium) dashed var(--nds-secondary);
|
||||
&:hover {
|
||||
background-color: var(--nds-secondary);
|
||||
border-bottom: var(--nds-border-width-medium) dashed var(--nds-accent);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--nds-primary);
|
||||
border-bottom: var(--nds-border-width-medium) dashed var(--nds-secondary);
|
||||
}
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-3xs);
|
||||
border-bottom: 1px solid transparent;
|
||||
transition:
|
||||
border-color 130ms ease,
|
||||
color 130ms ease;
|
||||
&:hover {
|
||||
border-bottom-color: var(--nds-primary);
|
||||
}
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px var(--nds-ring);
|
||||
border-radius: var(--nds-radius-xs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.loading-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-color: var(--nds-text);
|
||||
background-color: var(--nds-surface-2);
|
||||
overflow: hidden;
|
||||
border-radius: var(--nds-radius-sm);
|
||||
|
||||
border-radius: var(--nds-radius-full);
|
||||
&__progress {
|
||||
height: 100%;
|
||||
background-color: var(--nds-primary);
|
||||
transition: width 0.3s ease;
|
||||
border-radius: var(--nds-radius-sm);
|
||||
border-radius: var(--nds-radius-full);
|
||||
transition: width 0.35s ease;
|
||||
}
|
||||
|
||||
&__unknown {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -50%;
|
||||
width: 50%;
|
||||
left: 0;
|
||||
width: 40%;
|
||||
height: 100%;
|
||||
background-color: var(--nds-primary);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
animation: loadingBarIndeterminate 1.5s infinite;
|
||||
border-radius: var(--nds-radius-full);
|
||||
animation: nds-lb 1.3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loadingBarIndeterminate {
|
||||
from {
|
||||
left: -50%;
|
||||
}
|
||||
to {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
@keyframes nds-lb {
|
||||
0% {
|
||||
left: -40%;
|
||||
}
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
---
|
||||
export interface Props {
|
||||
type: 'known' | 'unknown';
|
||||
type: "known" | "unknown";
|
||||
percentage?: number;
|
||||
width?: string;
|
||||
width?: string;
|
||||
}
|
||||
const { type, percentage, width } = Astro.props;
|
||||
---
|
||||
<div class='loading-bar loading-bar--${type}' style={width ? `width: ${width}` : undefined}>
|
||||
{type === 'known' && percentage !== undefined ? (
|
||||
<div class='loading-bar__progress' style={`width: ${percentage}%`}></div>
|
||||
) : (
|
||||
<div class='loading-bar__unknown'></div>
|
||||
)}
|
||||
|
||||
<div
|
||||
class={`loading-bar loading-bar--${type}`}
|
||||
style={width ? `width: ${width}` : undefined}
|
||||
>
|
||||
{
|
||||
type === "known" && percentage !== undefined ? (
|
||||
<div class="loading-bar__progress" style={`width: ${percentage}%`} />
|
||||
) : (
|
||||
<div class="loading-bar__unknown" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use './loadingBar';
|
||||
</style>
|
||||
@use "./_loadingBar.scss";
|
||||
</style>
|
||||
|
||||
@@ -1,61 +1,50 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.notification {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--nds-spacing-sm) var(--nds-spacing-xs) ;
|
||||
border-radius: var(--nds-radius-md);
|
||||
@include text-sm;
|
||||
&__error {
|
||||
background-color: var(--nds-error-low);
|
||||
&__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--nds-error-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
color: var(--nds-error-high);
|
||||
}
|
||||
}
|
||||
|
||||
&__success {
|
||||
background-color: var(--nds-success-low);
|
||||
&__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--nds-success-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
color: var(--nds-success-high);
|
||||
}
|
||||
}
|
||||
|
||||
&__warning {
|
||||
background-color: var(--nds-warning-low);
|
||||
&__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--nds-warning-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
color: var(--nds-warning-high);
|
||||
}
|
||||
}
|
||||
|
||||
gap: var(--nds-spacing-sm);
|
||||
padding: var(--nds-spacing-sm) var(--nds-spacing-md);
|
||||
border-radius: var(--nds-radius-lg);
|
||||
background-color: var(--nds-surface);
|
||||
box-shadow: var(--nds-shadow-md);
|
||||
border-left: 3px solid var(--nds-info-medium);
|
||||
@include text-base;
|
||||
max-width: 400px;
|
||||
&__info {
|
||||
background-color: var(--nds-info-low);
|
||||
&__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--nds-info-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
color: var(--nds-info-high);
|
||||
border-left-color: var(--nds-info-medium);
|
||||
}
|
||||
&__success {
|
||||
border-left-color: var(--nds-success-medium);
|
||||
}
|
||||
&__warning {
|
||||
border-left-color: var(--nds-warning-medium);
|
||||
}
|
||||
&__error {
|
||||
border-left-color: var(--nds-error-medium);
|
||||
}
|
||||
&__info__content,
|
||||
&__success__content,
|
||||
&__warning__content,
|
||||
&__error__content {
|
||||
color: var(--nds-text);
|
||||
flex: 1;
|
||||
}
|
||||
&__info__close,
|
||||
&__success__close,
|
||||
&__warning__close,
|
||||
&__error__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
color: var(--nds-neutral);
|
||||
padding: 0;
|
||||
border-radius: var(--nds-radius-sm);
|
||||
transition: color 120ms ease;
|
||||
&:hover {
|
||||
color: var(--nds-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
---
|
||||
import { CloseIcon } from "../Icons";
|
||||
|
||||
|
||||
export interface Props {
|
||||
type: 'info' | 'success' | 'warning' | 'error';
|
||||
type: "info" | "success" | "warning" | "error";
|
||||
}
|
||||
const notificationUUID = `notification-${Math.random().toString(36).substr(2, 9)}`;
|
||||
const notificationUUID = `notification-${Math.random().toString(36).slice(2, 11)}`;
|
||||
const { type } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={`notification notification__${type}`} role="alert" id={notificationUUID}>
|
||||
<div class=`notification__${type}__content`>
|
||||
<div
|
||||
class={`notification notification__${type}`}
|
||||
role="alert"
|
||||
id={notificationUUID}
|
||||
>
|
||||
<div class={`notification__${type}__content`}>
|
||||
<slot />
|
||||
</div>
|
||||
<button class={`notification__${type}__close`} aria-label="Close notification" onclick={`document.getElementById('${notificationUUID}')?.remove()`}>
|
||||
<button
|
||||
class={`notification__${type}__close`}
|
||||
aria-label="Close notification"
|
||||
onclick={`document.getElementById('${notificationUUID}')?.remove()`}
|
||||
>
|
||||
<CloseIcon size={16} label="Close notification" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use './_notification.scss';
|
||||
</style>
|
||||
@use "./_notification.scss";
|
||||
</style>
|
||||
|
||||
@@ -1,33 +1,40 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--nds-spacing-xs);
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: fit-content;
|
||||
cursor: pointer;
|
||||
padding: var(--nds-spacing-2xs);
|
||||
@include text-base;
|
||||
|
||||
&--active {
|
||||
color: var(--nds-primary);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 120%;
|
||||
height: 1px;
|
||||
background-color: var(--nds-primary);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
border-bottom: 1px solid var(--nds-border);
|
||||
width: fit-content;
|
||||
&__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-sm);
|
||||
cursor: pointer;
|
||||
@include text-label;
|
||||
color: var(--nds-neutral);
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
transition:
|
||||
color 130ms ease,
|
||||
border-color 130ms ease;
|
||||
&:hover {
|
||||
color: var(--nds-text);
|
||||
}
|
||||
}
|
||||
&--active {
|
||||
color: var(--nds-text);
|
||||
border-bottom-color: var(--nds-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.tab__content {
|
||||
padding-top: var(--nds-spacing-md);
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,13 @@
|
||||
.toggle {
|
||||
background-color: var(--nds-disabled);
|
||||
border-radius: var(--nds-radius-full);
|
||||
padding: var(--nds-spacing-2xs);
|
||||
width: 50px;
|
||||
height: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
cursor: pointer;
|
||||
transition: background-color 160ms ease;
|
||||
position: relative;
|
||||
&-switch {
|
||||
color: var(--nds-text);
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-color: var(--nds-text);
|
||||
border-radius: var(--nds-radius-full);
|
||||
position: absolute;
|
||||
left: var(--nds-spacing-2xs);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: left 160ms ease, background-color 160ms ease, transform 160ms ease;
|
||||
}
|
||||
&-active {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
background-color: var(--nds-primary);
|
||||
.toggle-switch {
|
||||
left: calc(100% - var(--nds-spacing-2xs) - 15px);
|
||||
}
|
||||
}
|
||||
--w: 44px; --h: 24px; --pad: 3px;
|
||||
position: relative; width: var(--w); height: var(--h);
|
||||
background-color: var(--nds-disabled); border-radius: var(--nds-radius-full);
|
||||
padding: var(--pad); display: flex; align-items: center; cursor: pointer;
|
||||
transition: background-color 180ms ease;
|
||||
&-switch { width: calc(var(--h) - var(--pad) * 2); height: calc(var(--h) - var(--pad) * 2);
|
||||
background-color: #fff; border-radius: var(--nds-radius-full); box-shadow: var(--nds-shadow-sm);
|
||||
transition: transform 180ms cubic-bezier(.34,1.4,.6,1); }
|
||||
&-active { background-color: var(--nds-primary); }
|
||||
&-active .toggle-switch { transform: translateX(calc(var(--w) - var(--h))); }
|
||||
}
|
||||
toggle-switch:focus-visible .toggle { box-shadow: 0 0 0 3px var(--nds-ring); outline: none; }
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
import { boolean } from 'astro:schema';
|
||||
|
||||
export interface Props {
|
||||
'data-checked': boolean;
|
||||
'data-checked'?: boolean | string;
|
||||
name?: string;
|
||||
id: string;
|
||||
}
|
||||
@@ -10,7 +8,7 @@ export interface Props {
|
||||
const { 'data-checked': dataChecked = 'false', name = 'toggle', id } = Astro.props;
|
||||
---
|
||||
|
||||
<toggle-switch data-checked={dataChecked} data-name={name} id={id}>
|
||||
<toggle-switch data-checked={String(dataChecked)} data-name={name} id={id}>
|
||||
<div class="toggle">
|
||||
<div class="toggle-switch"></div>
|
||||
</div>
|
||||
@@ -35,7 +33,7 @@ const { 'data-checked': dataChecked = 'false', name = 'toggle', id } = Astro.pro
|
||||
|
||||
this.dispatchEvent(new CustomEvent('change', {
|
||||
detail: { checked: this._checked, name: this.dataset.name },
|
||||
bubbles: true,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
}
|
||||
@@ -59,4 +57,4 @@ const { 'data-checked': dataChecked = 'false', name = 'toggle', id } = Astro.pro
|
||||
|
||||
<style lang="scss">
|
||||
@use './_toggle.scss';
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
gap: var(--nds-spacing-sm);
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.pagination-number {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 var(--nds-spacing-2xs);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--nds-radius-full);
|
||||
border-radius: var(--nds-radius-md);
|
||||
cursor: pointer;
|
||||
@include text-label;
|
||||
color: var(--nds-text);
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
transition: all 120ms ease;
|
||||
user-select: none;
|
||||
&:hover:not(.pagination-number__disabled):not(.pagination-number__selected) {
|
||||
background-color: var(--nds-secondary);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:active:not(.pagination-number__disabled):not(.pagination-number__selected) {
|
||||
background-color: var(--nds-accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__disabled {
|
||||
background-color: var(--nds-disabled);
|
||||
cursor: not-allowed;
|
||||
border-color: var(--nds-border-strong);
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
&__selected {
|
||||
background-color: var(--nds-primary);
|
||||
cursor: pointer;
|
||||
color: var(--nds-on-primary);
|
||||
}
|
||||
}
|
||||
&__disabled {
|
||||
color: var(--nds-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
min-height: 84px;
|
||||
padding: var(--nds-spacing-sm);
|
||||
resize: vertical;
|
||||
width: 100% !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
&__charCount {
|
||||
|
||||
+12
-24
@@ -1,29 +1,17 @@
|
||||
---
|
||||
import '../styles/index.scss';
|
||||
interface Props { title?: string; }
|
||||
const { title = 'Nova Design System' } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Nova Design System</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
font-family: var(--nds-font-family-base);
|
||||
background-color: var(--nds-page-low);
|
||||
color: var(--nds-default);
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+393
-354
@@ -1,23 +1,45 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Notification from '../components/Notifications/notification.astro';
|
||||
|
||||
// Core
|
||||
import Button from '../components/Button/button.astro';
|
||||
import Badge from '../components/Badge/badge.astro';
|
||||
import Avatar from '../components/Avatar/avatar.astro';
|
||||
|
||||
// Forms
|
||||
import Toggle from '../components/Toggle/toggle.astro';
|
||||
import Checkbox from '../components/Checkbox/checkbox.astro';
|
||||
import Radio from '../components/Radio/radio.astro';
|
||||
import TextField from '../components/textField/textField.astro';
|
||||
import Select from '../components/Select/select.astro';
|
||||
import SelectOption from '../components/Select/selectOption.astro';
|
||||
import NumericStepper from '../components/numericStepper/numericStepper.astro';
|
||||
|
||||
// Navigation
|
||||
import Tab from '../components/Tabs/tab.astro';
|
||||
import TabItem from '../components/Tabs/tabItem.astro';
|
||||
import TabContent from '../components/Tabs/tabContent.astro';
|
||||
import Button from '../components/Button/button.astro';
|
||||
import Link from '../components/Link/link.astro';
|
||||
import Breadcrumb from '../components/Breadcrumb/breadcrumb.astro';
|
||||
import BreadcrumbItem from '../components/Breadcrumb/breadcrumbItem.astro';
|
||||
import Pagination from '../components/pagination/pagination.astro';
|
||||
import PaginationNumber from '../components/pagination/paginationNumber.astro';
|
||||
import Navbar from '../components/Navbar/navbar.astro';
|
||||
import Sidebar from '../components/Sidebar/sidebar.astro';
|
||||
import SidebarItem from '../components/Sidebar/sidebarItem.astro';
|
||||
|
||||
// Data
|
||||
import Card from '../components/Card/card.astro';
|
||||
import ListItem from '../components/ListItem/listItem.astro';
|
||||
import ListItemTitle from '../components/ListItem/listItemTitle.astro';
|
||||
import ListItemSubtitle from '../components/ListItem/listItemSubtitle.astro';
|
||||
|
||||
// Feedback
|
||||
import Notification from '../components/Notifications/notification.astro';
|
||||
import LoadingBar from '../components/LoadingBar/loadingBar.astro';
|
||||
import NumericStepper from '../components/numericStepper/numericStepper.astro';
|
||||
import Avatar from '../components/Avatar/avatar.astro';
|
||||
import Select from '../components/Select/select.astro';
|
||||
import SelectOption from '../components/Select/selectOption.astro';
|
||||
import TextField from '../components/textField/textField.astro';
|
||||
import Pagination from '../components/pagination/pagination.astro';
|
||||
import PaginationNumber from '../components/pagination/paginationNumber.astro';
|
||||
import Tooltip from '../components/Tooltip/tooltip.astro';
|
||||
import Modal from '../components/Modal/modal.astro';
|
||||
|
||||
import {
|
||||
Arrow2Icon, BinIcon, BurgerIcon, CalendarIcon, CheckIcon, CloseIcon,
|
||||
CodeIcon, CubeIcon, DownloadIcon, FilterIcon, HelpIcon, HomeIcon,
|
||||
@@ -26,15 +48,204 @@ import {
|
||||
UploadIcon,
|
||||
} from '../components/Icons/index.ts';
|
||||
|
||||
const initialChecked = true;
|
||||
const tones = ['primary', 'neutral', 'success', 'warning', 'error', 'info'] as const;
|
||||
const buttonTypes = ['primary', 'secondary', 'ghost', 'danger'] as const;
|
||||
const sizes = ['sm', 'md', 'lg'] as const;
|
||||
const libraryIcons = [
|
||||
['Profile', ProfileIcon], ['Search', SearchIcon], ['Settings', SettingsIcon],
|
||||
['Home', HomeIcon], ['Overview', OverviewIcon], ['Stats', StatsIcon],
|
||||
['Share', ShareIcon], ['Shield', ShieldIcon], ['Download', DownloadIcon],
|
||||
['Upload', UploadIcon], ['Filter', FilterIcon], ['Bin', BinIcon],
|
||||
['Burger', BurgerIcon], ['Calendar', CalendarIcon], ['Check', CheckIcon],
|
||||
['Close', CloseIcon], ['Code', CodeIcon], ['Cube', CubeIcon],
|
||||
['Help', HelpIcon], ['Link', LinkIcon], ['Minus', MinusIcon],
|
||||
['More', MoreIcon], ['Plus', PlusIcon], ['Sort', SortIcon],
|
||||
] as const;
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<main>
|
||||
<h1>Nova Design System</h1>
|
||||
<section>
|
||||
<div class="gallery">
|
||||
<header class="gallery__head">
|
||||
<div class="gallery__brand">
|
||||
<span class="gallery__mark"></span>
|
||||
<span class="gallery__name">Nova Design System</span>
|
||||
<Badge type="primary" variant="soft">v0.06</Badge>
|
||||
</div>
|
||||
<Button type="secondary" size="sm" id="theme-toggle">Toggle theme</Button>
|
||||
</header>
|
||||
|
||||
<!-- BUTTON -->
|
||||
<section class="block" id="button">
|
||||
<h2>Button</h2>
|
||||
<p class="block__desc">Variants, sizes, icon and disabled states.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Variants × md</span><div class="vgroup__body">
|
||||
{buttonTypes.map((t) => <Button type={t} size="md">{t[0].toUpperCase() + t.slice(1)}</Button>)}
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Sizes (primary)</span><div class="vgroup__body">
|
||||
{sizes.map((s) => <Button type="primary" size={s}>Size {s}</Button>)}
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">With icon · icon-only</span><div class="vgroup__body">
|
||||
<Button type="primary" size="md"><PlusIcon slot="icon-left" size={16} />New service</Button>
|
||||
<Button type="secondary" size="md">Filter<FilterIcon slot="icon-right" size={16} /></Button>
|
||||
<Button type="ghost" size="md" icon={true}><MoreIcon size={18} /></Button>
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Disabled</span><div class="vgroup__body">
|
||||
{buttonTypes.map((t) => <Button type={t} size="md" disabled>{t}</Button>)}
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- BADGE -->
|
||||
<section class="block" id="badge">
|
||||
<h2>Badge</h2>
|
||||
<p class="block__desc">Tones × soft / solid.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Soft</span><div class="vgroup__body">
|
||||
{tones.map((t) => <Badge type={t} variant="soft">{t}</Badge>)}
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Solid</span><div class="vgroup__body">
|
||||
{tones.map((t) => <Badge type={t} variant="solid">{t}</Badge>)}
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- SELECTION CONTROLS -->
|
||||
<section class="block" id="controls">
|
||||
<h2>Toggle · Checkbox · Radio</h2>
|
||||
<p class="block__desc">Selection controls in every state.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Toggle — off / on</span><div class="vgroup__body">
|
||||
<Toggle id="tg-off" />
|
||||
<Toggle id="tg-on" data-checked={true} />
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Checkbox</span><div class="vgroup__body">
|
||||
<Checkbox id="cb1">Unchecked</Checkbox>
|
||||
<Checkbox id="cb2" checked>Checked</Checkbox>
|
||||
<Checkbox id="cb3" checked disabled>Disabled</Checkbox>
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Radio group</span><div class="vgroup__body" style="flex-direction:column;align-items:flex-start;">
|
||||
<Radio id="rb1" name="plan" checked>On-demand</Radio>
|
||||
<Radio id="rb2" name="plan">Reserved</Radio>
|
||||
<Radio id="rb3" name="plan" disabled>Spot</Radio>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- TEXTFIELD -->
|
||||
<section class="block" id="textfield">
|
||||
<h2>TextField</h2>
|
||||
<p class="block__desc">Text, email, password, textarea.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Fields</span><div class="vgroup__body" style="flex-direction:column;align-items:stretch;max-width:360px;">
|
||||
<TextField label="Service name" placeholder="api-gateway" />
|
||||
<TextField label="Email" type="email" placeholder="you@nova.dev" />
|
||||
<TextField label="Password" type="password" placeholder="••••••••" />
|
||||
<TextField label="Message" type="textarea" placeholder="Type here…" max={120} />
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- SELECT -->
|
||||
<section class="block" id="select">
|
||||
<h2>Select</h2>
|
||||
<p class="block__desc">Default and disabled.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Select</span><div class="vgroup__body">
|
||||
<Select default="option3">
|
||||
<SelectOption value="option1">Option 1</SelectOption>
|
||||
<SelectOption value="option2">Option 2</SelectOption>
|
||||
<SelectOption value="option3">Option 3</SelectOption>
|
||||
<SelectOption value="option4">Option 4</SelectOption>
|
||||
<SelectOption value="option5" disabled>Option 5</SelectOption>
|
||||
</Select>
|
||||
<Select disabled>
|
||||
<SelectOption value="option1">Disabled</SelectOption>
|
||||
</Select>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- NUMERIC STEPPER -->
|
||||
<section class="block" id="stepper">
|
||||
<h2>Numeric stepper</h2>
|
||||
<p class="block__desc">Increment / decrement control.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Stepper</span><div class="vgroup__body">
|
||||
<NumericStepper min={-15} max={10} value={5} step={5} id="ns1" />
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- AVATAR -->
|
||||
<section class="block" id="avatar">
|
||||
<h2>Avatar</h2>
|
||||
<p class="block__desc">Photo and initials.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Types</span><div class="vgroup__body">
|
||||
<Avatar type="photo" name="John Doe" image="https://i.pravatar.cc/80?img=12" />
|
||||
<Avatar type="initials" name="Ava Stone" />
|
||||
<Avatar type="initials" name="Jane Smith" />
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- LOADING BAR -->
|
||||
<section class="block" id="loading">
|
||||
<h2>Loading bar</h2>
|
||||
<p class="block__desc">Determinate and indeterminate.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Known 75% · indeterminate</span><div class="vgroup__body" style="flex-direction:column;align-items:stretch;">
|
||||
<LoadingBar type="known" percentage={75} width="100%" />
|
||||
<LoadingBar type="unknown" width="100%" />
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- LIST ITEM -->
|
||||
<section class="block" id="listitem">
|
||||
<h2>List item</h2>
|
||||
<p class="block__desc">Title and subtitle rows.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">List</span><div class="vgroup__body" style="flex-direction:column;align-items:stretch;">
|
||||
<ListItem>
|
||||
<ListItemTitle>api-gateway</ListItemTitle>
|
||||
<ListItemSubtitle>Edge routing · eu-west-1</ListItemSubtitle>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemTitle>postgres-main</ListItemTitle>
|
||||
<ListItemSubtitle>Database · eu-west-1</ListItemSubtitle>
|
||||
</ListItem>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- LINK -->
|
||||
<section class="block" id="link">
|
||||
<h2>Link</h2>
|
||||
<p class="block__desc">Inline and external.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Links</span><div class="vgroup__body">
|
||||
<Link url="https://example.com">Documentation</Link>
|
||||
<Link url="https://example.com" blank={true}>Open dashboard</Link>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- TABS -->
|
||||
<section class="block" id="tabs">
|
||||
<h2>Tabs</h2>
|
||||
<p class="block__desc">Underline tabs with panels.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Tabs</span><div class="vgroup__body" style="flex-direction:column;align-items:stretch;">
|
||||
<Tab id="gallery-tab" defaultActive="images">
|
||||
<TabItem id="images">Images</TabItem>
|
||||
<TabItem id="videos">Videos</TabItem>
|
||||
<TabItem id="docs">Documents</TabItem>
|
||||
</Tab>
|
||||
<TabContent id="gallery-tab" itemId="images"><p>🖼️ Image gallery content.</p></TabContent>
|
||||
<TabContent id="gallery-tab" itemId="videos"><p>🎬 Video player here.</p></TabContent>
|
||||
<TabContent id="gallery-tab" itemId="docs"><p>📄 Document list here.</p></TabContent>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- BREADCRUMB -->
|
||||
<section class="block" id="breadcrumb">
|
||||
<h2>Breadcrumb</h2>
|
||||
<p class="block__desc">Hierarchical navigation.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Breadcrumb</span><div class="vgroup__body">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem href="/">Home</BreadcrumbItem>
|
||||
<BreadcrumbItem href="/services">Services</BreadcrumbItem>
|
||||
<BreadcrumbItem current>api-gateway</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<!-- PAGINATION -->
|
||||
<section class="block" id="pagination">
|
||||
<h2>Pagination</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<p class="block__desc">Page numbers with disabled edges.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Pagination</span><div class="vgroup__body">
|
||||
<Pagination defaultPage={3}>
|
||||
<PaginationNumber>1</PaginationNumber>
|
||||
<PaginationNumber>2</PaginationNumber>
|
||||
@@ -42,364 +253,192 @@ const initialChecked = true;
|
||||
<PaginationNumber>4</PaginationNumber>
|
||||
<PaginationNumber disabled>5</PaginationNumber>
|
||||
</Pagination>
|
||||
</div>
|
||||
</div></div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>TextField</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<TextField label="Email" type="email" placeholder="Enter your email" />
|
||||
<TextField label="Password" type="password" placeholder="Enter your password" />
|
||||
<TextField label="Textarea" type="textarea" placeholder="Enter your message" max={100} />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Select</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Select default="option3">
|
||||
<SelectOption value="option1">Option 1</SelectOption>
|
||||
<SelectOption value="option2">Option 2</SelectOption>
|
||||
<SelectOption value="option3" >Option 3</SelectOption>
|
||||
<SelectOption value="option4" >Option 4</SelectOption>
|
||||
<SelectOption value="option5" >Option 5</SelectOption>
|
||||
<SelectOption value="option6" >Option 6</SelectOption>
|
||||
<SelectOption value="option7" >Option 7</SelectOption>
|
||||
<SelectOption value="option8" >Option 8</SelectOption>
|
||||
<SelectOption value="option9" disabled>Option 9</SelectOption>
|
||||
</Select>
|
||||
<Select disabled>
|
||||
<SelectOption value="option1">Option 1</SelectOption>
|
||||
</Select>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Avatar</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Avatar image="https://static.vecteezy.com/system/resources/thumbnails/057/068/323/small/single-fresh-red-strawberry-on-table-green-background-food-fruit-sweet-macro-juicy-plant-image-photo.jpg" name="John Doe" type="photo"/>
|
||||
<Avatar name="Jane Smith" type="initials"/>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Numeric Stepper</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<NumericStepper min={-15} max={10} value={5} step={5} id="my-numeric-stepper"/>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Loading Bar</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<LoadingBar type="known" percentage={75} width="500px" />
|
||||
<LoadingBar type="unknown" width="500px" />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>List Item</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<ListItem>
|
||||
<ListItemTitle>List Item Title</ListItemTitle>
|
||||
<ListItemSubtitle>List Item Subtitle</ListItemSubtitle>
|
||||
</ListItem>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Link</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Link url="https://example.com">Example Link</Link>
|
||||
<Link url="https://example.com" blank={true}>Example Link (opens in new tab)</Link>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Button</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Button type="primary">Primary Button</Button>
|
||||
<Button type="secondary">Secondary Button</Button>
|
||||
<Button type="primary" disabled>disabled</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Tabs</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Tab id="my-tab" defaultActive="images">
|
||||
<TabItem id="images">images</TabItem>
|
||||
<TabItem id="videos">Videos</TabItem>
|
||||
<TabItem id="docs">Documents</TabItem>
|
||||
</Tab>
|
||||
<TabContent id="my-tab" itemId="images">
|
||||
<p>my images</p>
|
||||
</TabContent>
|
||||
|
||||
<TabContent id="my-tab" itemId="videos">
|
||||
<p>Video player here</p>
|
||||
</TabContent>
|
||||
<!-- NOTIFICATION -->
|
||||
<section class="block" id="notification">
|
||||
<h2>Notification</h2>
|
||||
<p class="block__desc">Info, success, warning and error.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">All types</span><div class="vgroup__body" style="flex-direction:column;align-items:stretch;max-width:440px;">
|
||||
<Notification type="info">Information about the operation.</Notification>
|
||||
<Notification type="success">The operation completed successfully.</Notification>
|
||||
<Notification type="warning">A warning occurred. Please check the details.</Notification>
|
||||
<Notification type="error">An error occurred. Please try again.</Notification>
|
||||
</div></div>
|
||||
</section>
|
||||
|
||||
<TabContent id="my-tab" itemId="docs">
|
||||
<p>Document list here</p>
|
||||
</TabContent>
|
||||
</div>
|
||||
<!-- TOOLTIP -->
|
||||
<section class="block" id="tooltip">
|
||||
<h2>Tooltip</h2>
|
||||
<p class="block__desc">Four positions — hover the buttons.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Positions</span><div class="vgroup__body">
|
||||
<Tooltip label="Tooltip top" position="top"><Button type="secondary" size="md">Top</Button></Tooltip>
|
||||
<Tooltip label="Tooltip bottom" position="bottom"><Button type="secondary" size="md">Bottom</Button></Tooltip>
|
||||
<Tooltip label="Tooltip left" position="left"><Button type="secondary" size="md">Left</Button></Tooltip>
|
||||
<Tooltip label="Tooltip right" position="right"><Button type="secondary" size="md">Right</Button></Tooltip>
|
||||
</div></div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Toggle</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Toggle data-checked={initialChecked} data-name="notifications" id="my-toggle" />
|
||||
<p id="toggle-state">State: {initialChecked ? 'Checked' : 'Unchecked'}</p>
|
||||
</div>
|
||||
|
||||
<!-- CARD -->
|
||||
<section class="block" id="card">
|
||||
<h2>Card</h2>
|
||||
<p class="block__desc">With cover, plain and with footer.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Cards</span><div class="vgroup__body" style="align-items:flex-start;">
|
||||
<Card title="api-gateway" subtitle="Edge routing · 99.98% uptime">
|
||||
<div slot="footer"><Badge type="success" variant="soft">Live</Badge></div>
|
||||
</Card>
|
||||
<Card title="Quick note" subtitle="A plain card without a cover image." />
|
||||
<Card title="Deploy" subtitle="Ship build 4821 to production.">
|
||||
<div slot="footer">
|
||||
<Button type="ghost" size="sm">Cancel</Button>
|
||||
<Button type="primary" size="sm">Deploy</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div></div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Notifications — Error / Warning / Success / Info</h2>
|
||||
<div style="max-width: 400px; display: flex; flex-direction: column; gap: var(--nds-spacing-md);">
|
||||
<Notification type="error">
|
||||
An error occurred during the operation. Please try again.
|
||||
</Notification>
|
||||
<Notification type="success">
|
||||
The operation was completed successfully.
|
||||
</Notification>
|
||||
<Notification type="warning">
|
||||
A warning occurred during the operation. Please check the details.
|
||||
</Notification>
|
||||
<Notification type="info">
|
||||
Information about the operation.
|
||||
</Notification>
|
||||
</div>
|
||||
|
||||
<!-- APP CHROME -->
|
||||
<section class="block" id="chrome">
|
||||
<h2>Navbar · Sidebar</h2>
|
||||
<p class="block__desc">App chrome.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Navbar</span><div class="vgroup__body" style="align-items:stretch;">
|
||||
<Navbar brand="Nova">
|
||||
<a class="navbar__link navbar__link--active" href="#">Services</a>
|
||||
<a class="navbar__link" href="#">Incidents</a>
|
||||
<a class="navbar__link" href="#">Settings</a>
|
||||
<Avatar slot="right" type="initials" name="Ava Stone" />
|
||||
</Navbar>
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Sidebar</span><div class="vgroup__body">
|
||||
<Sidebar header="Nova">
|
||||
<SidebarItem href="#" active><OverviewIcon slot="icon" size={18} />Overview</SidebarItem>
|
||||
<SidebarItem href="#"><StatsIcon slot="icon" size={18} />Metrics</SidebarItem>
|
||||
<SidebarItem href="#"><ShieldIcon slot="icon" size={18} />Security</SidebarItem>
|
||||
<SidebarItem href="#"><SettingsIcon slot="icon" size={18} />Settings</SidebarItem>
|
||||
</Sidebar>
|
||||
</div></div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Icon — 16 / 24 / 32</h2>
|
||||
<div class="icon-grid">
|
||||
<div class="icon-row">
|
||||
<Arrow2Icon size={16} orientation="up" />
|
||||
<Arrow2Icon size={16} orientation="right" />
|
||||
<Arrow2Icon size={16} orientation="down" />
|
||||
<Arrow2Icon size={16} orientation="left" />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<Arrow2Icon size={24} orientation="up" />
|
||||
<Arrow2Icon size={24} orientation="right" />
|
||||
<Arrow2Icon size={24} orientation="down" />
|
||||
<Arrow2Icon size={24} orientation="left" />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<Arrow2Icon size={32} orientation="up" />
|
||||
<Arrow2Icon size={32} orientation="right" />
|
||||
<Arrow2Icon size={32} orientation="down" />
|
||||
<Arrow2Icon size={32} orientation="left" />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<ProfileIcon size={16} />
|
||||
<ProfileIcon size={24} />
|
||||
<ProfileIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<ShareIcon size={16} />
|
||||
<ShareIcon size={24} />
|
||||
<ShareIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<SearchIcon size={16} />
|
||||
<SearchIcon size={24} />
|
||||
<SearchIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<CloseIcon size={16} />
|
||||
<CloseIcon size={24} />
|
||||
<CloseIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<UploadIcon size={16} />
|
||||
<UploadIcon size={24} />
|
||||
<UploadIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<BinIcon size={16} />
|
||||
<BinIcon size={24} />
|
||||
<BinIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<BurgerIcon size={16} />
|
||||
<BurgerIcon size={24} />
|
||||
<BurgerIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<CalendarIcon size={16} />
|
||||
<CalendarIcon size={24} />
|
||||
<CalendarIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<CheckIcon size={16} />
|
||||
<CheckIcon size={24} />
|
||||
<CheckIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<CodeIcon size={16} />
|
||||
<CodeIcon size={24} />
|
||||
<CodeIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<CubeIcon size={16} />
|
||||
<CubeIcon size={24} />
|
||||
<CubeIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<DownloadIcon size={16} />
|
||||
<DownloadIcon size={24} />
|
||||
<DownloadIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<FilterIcon size={16} />
|
||||
<FilterIcon size={24} />
|
||||
<FilterIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<HelpIcon size={16} />
|
||||
<HelpIcon size={24} />
|
||||
<HelpIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<HomeIcon size={16} />
|
||||
<HomeIcon size={24} />
|
||||
<HomeIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<LinkIcon size={16} />
|
||||
<LinkIcon size={24} />
|
||||
<LinkIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<MinusIcon size={16} />
|
||||
<MinusIcon size={24} />
|
||||
<MinusIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<MoreIcon size={16} />
|
||||
<MoreIcon size={24} />
|
||||
<MoreIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<OverviewIcon size={16} />
|
||||
<OverviewIcon size={24} />
|
||||
<OverviewIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<PlusIcon size={16} />
|
||||
<PlusIcon size={24} />
|
||||
<PlusIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<SettingsIcon size={16} />
|
||||
<SettingsIcon size={24} />
|
||||
<SettingsIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<ShieldIcon size={16} />
|
||||
<ShieldIcon size={24} />
|
||||
<ShieldIcon size={32} />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<SortIcon size={16} variant="default" />
|
||||
<SortIcon size={16} variant="ascend" />
|
||||
<SortIcon size={16} variant="descend" />
|
||||
<SortIcon size={24} variant="default" />
|
||||
<SortIcon size={24} variant="ascend" />
|
||||
<SortIcon size={24} variant="descend" />
|
||||
<SortIcon size={32} variant="default" />
|
||||
<SortIcon size={32} variant="ascend" />
|
||||
<SortIcon size={32} variant="descend" />
|
||||
</div>
|
||||
<div class="icon-row">
|
||||
<StatsIcon size={16} />
|
||||
<StatsIcon size={24} />
|
||||
<StatsIcon size={32} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL -->
|
||||
<section class="block" id="modal">
|
||||
<h2>Modal</h2>
|
||||
<p class="block__desc">Overlay dialog.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Dialog</span><div class="vgroup__body">
|
||||
<Button type="primary" size="md" data-modal-open="demo-modal">Open modal</Button>
|
||||
</div></div>
|
||||
<Modal id="demo-modal" title="Confirm deploy">
|
||||
Ship build 4821 to production? This rolls out to all edge regions.
|
||||
<Fragment slot="footer">
|
||||
<Button type="ghost" size="md" data-modal-close>Cancel</Button>
|
||||
<Button type="primary" size="md" data-modal-close>Deploy</Button>
|
||||
</Fragment>
|
||||
</Modal>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ICONS -->
|
||||
<section class="block" id="icons">
|
||||
<h2>Icons</h2>
|
||||
<p class="block__desc">Library at 24px, plus arrow orientations and sizes.</p>
|
||||
<div class="vgroup"><span class="vgroup__label">Arrow — orientations × 16/24/32</span><div class="vgroup__body">
|
||||
{[16, 24, 32].map((s) => (
|
||||
<span class="icon-line">
|
||||
<Arrow2Icon size={s} orientation="up" />
|
||||
<Arrow2Icon size={s} orientation="right" />
|
||||
<Arrow2Icon size={s} orientation="down" />
|
||||
<Arrow2Icon size={s} orientation="left" />
|
||||
</span>
|
||||
))}
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Library (24px)</span><div class="vgroup__body" style="display:block;">
|
||||
<div class="icon-grid">
|
||||
{libraryIcons.map(([name, Icon]) => (
|
||||
<div class="icon-cell"><Icon size={24} /><span>{name}</span></div>
|
||||
))}
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="vgroup"><span class="vgroup__label">Sizes — 16 / 24 / 32</span><div class="vgroup__body">
|
||||
{[SearchIcon, SettingsIcon, ShareIcon, DownloadIcon].map((Icon) => (
|
||||
<span class="icon-line">
|
||||
<Icon size={16} /><Icon size={24} /><Icon size={32} />
|
||||
</span>
|
||||
))}
|
||||
</div></div>
|
||||
</section>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
const toggle = document.getElementById('my-toggle') as any;
|
||||
const stateLabel = document.getElementById('toggle-state');
|
||||
|
||||
// Défaut: thème sombre
|
||||
if (!document.documentElement.hasAttribute('data-theme')) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
if (stateLabel) stateLabel.textContent = 'Thème : Sombre';
|
||||
|
||||
toggle?.addEventListener('change', (e: CustomEvent) => {
|
||||
const { checked } = e.detail;
|
||||
|
||||
if (checked) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
if (stateLabel) stateLabel.textContent = 'Thème : Sombre';
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-theme', 'light');
|
||||
if (stateLabel) stateLabel.textContent = 'Thème : Clair';
|
||||
}
|
||||
});
|
||||
// Theme toggle (persisted)
|
||||
const KEY = 'nova-theme';
|
||||
const saved = localStorage.getItem(KEY);
|
||||
if (saved) document.documentElement.setAttribute('data-theme', saved);
|
||||
document.getElementById('theme-toggle')?.addEventListener('click', () => {
|
||||
const dark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
const next = dark ? 'light' : 'dark';
|
||||
document.documentElement.setAttribute('data-theme', next);
|
||||
localStorage.setItem(KEY, next);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="scss">
|
||||
@use '../styles/tokens/typography' as *;
|
||||
|
||||
body {
|
||||
background-color: var(--nds-background);
|
||||
color: var(--nds-text);
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 900px;
|
||||
.gallery {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: var(--nds-spacing-xl) var(--nds-spacing-md);
|
||||
padding: var(--nds-spacing-xl) var(--nds-spacing-lg) var(--nds-spacing-3xl);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--nds-type-h1-size);
|
||||
font-weight: var(--nds-type-h1-weight);
|
||||
line-height: var(--nds-type-h1-line-height);
|
||||
letter-spacing: var(--nds-type-h1-letter-spacing);
|
||||
margin-bottom: var(--nds-spacing-xl);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--nds-type-h2-size);
|
||||
font-weight: var(--nds-type-h2-weight);
|
||||
line-height: var(--nds-type-h2-line-height);
|
||||
letter-spacing: var(--nds-type-h2-letter-spacing);
|
||||
color: var(--nds-text-muted);
|
||||
margin-bottom: var(--nds-spacing-md);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: var(--nds-spacing-2xl);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-sm);
|
||||
}
|
||||
|
||||
.icon-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--nds-spacing-xs);
|
||||
}
|
||||
|
||||
.icon-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-lg);
|
||||
color: var(--nds-text);
|
||||
}
|
||||
|
||||
.icon-name {
|
||||
font-size: var(--nds-type-body-small-size);
|
||||
color: var(--nds-text-muted);
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icon-sizes {
|
||||
.gallery__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--nds-spacing-md);
|
||||
color: var(--nds-default);
|
||||
margin-bottom: var(--nds-spacing-2xl);
|
||||
padding-bottom: var(--nds-spacing-md);
|
||||
border-bottom: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
}
|
||||
.gallery__brand { display: flex; align-items: center; gap: var(--nds-spacing-xs); }
|
||||
.gallery__mark {
|
||||
width: 26px; height: 26px; border-radius: var(--nds-radius-md);
|
||||
background: linear-gradient(135deg, var(--nds-primary), var(--nds-accent));
|
||||
}
|
||||
.gallery__name { @include text-xl; }
|
||||
|
||||
.block { margin-bottom: var(--nds-spacing-2xl); scroll-margin-top: var(--nds-spacing-lg); }
|
||||
.block > h2 { @include text-2xl; margin: 0 0 var(--nds-spacing-3xs); }
|
||||
.block__desc { color: var(--nds-neutral); @include text-base; margin: 0 0 var(--nds-spacing-lg); }
|
||||
|
||||
.vgroup {
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
border-radius: var(--nds-radius-lg);
|
||||
background: var(--nds-surface);
|
||||
overflow: hidden;
|
||||
& + & { margin-top: var(--nds-spacing-md); }
|
||||
}
|
||||
.vgroup__label {
|
||||
display: block;
|
||||
@include text-sm; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
|
||||
color: var(--nds-neutral);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-md);
|
||||
border-bottom: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
background: color-mix(in srgb, var(--nds-text) 2.5%, var(--nds-surface));
|
||||
}
|
||||
.vgroup__body {
|
||||
display: flex; flex-wrap: wrap; align-items: center;
|
||||
gap: var(--nds-spacing-md);
|
||||
padding: var(--nds-spacing-lg) var(--nds-spacing-md);
|
||||
}
|
||||
|
||||
.icon-line { display: inline-flex; align-items: center; gap: var(--nds-spacing-md); color: var(--nds-text); }
|
||||
.icon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--nds-spacing-xs); width: 100%; }
|
||||
.icon-cell {
|
||||
display: flex; flex-direction: column; align-items: center; gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-md) var(--nds-spacing-xs);
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
border-radius: var(--nds-radius-md);
|
||||
color: var(--nds-text);
|
||||
span { @include text-sm; color: var(--nds-neutral); font-family: var(--nds-font-mono); }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user