Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 027cec577d | |||
| 6e077aaeed | |||
| 1d9001705f | |||
| 1878ec3a0e | |||
| b48581b089 | |||
| 3593305512 | |||
| 6967f3c7cd | |||
| dea517fbe5 | |||
| cc54ae46ff |
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@unkn0wndo3s/nova-design-system",
|
||||
"type": "module",
|
||||
"version": "0.06.1",
|
||||
"version": "1.00.0",
|
||||
"main": "./src/index.ts",
|
||||
"description": "Nova Design System — Astro component library",
|
||||
"engines": {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
padding: 3px var(--nds-spacing-xs);
|
||||
border-radius: var(--nds-radius-full);
|
||||
@include text-sm;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
&__primary {
|
||||
--c: var(--nds-primary);
|
||||
--on: var(--nds-on-primary);
|
||||
--soft: var(--nds-primary-soft);
|
||||
}
|
||||
&__neutral {
|
||||
--c: var(--nds-neutral);
|
||||
--on: var(--nds-surface);
|
||||
--soft: color-mix(in srgb, var(--nds-neutral) 16%, transparent);
|
||||
}
|
||||
&__success {
|
||||
--c: var(--nds-success-medium);
|
||||
--on: #fff;
|
||||
--soft: var(--nds-success-low);
|
||||
}
|
||||
&__warning {
|
||||
--c: var(--nds-warning-medium);
|
||||
--on: #fff;
|
||||
--soft: var(--nds-warning-low);
|
||||
}
|
||||
&__error {
|
||||
--c: var(--nds-error-medium);
|
||||
--on: #fff;
|
||||
--soft: var(--nds-error-low);
|
||||
}
|
||||
&__info {
|
||||
--c: var(--nds-info-medium);
|
||||
--on: #fff;
|
||||
--soft: var(--nds-info-low);
|
||||
}
|
||||
&--soft {
|
||||
background-color: var(--soft);
|
||||
color: var(--c);
|
||||
}
|
||||
&--solid {
|
||||
background-color: var(--c);
|
||||
color: var(--on);
|
||||
}
|
||||
&__dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: var(--nds-radius-full);
|
||||
background-color: currentColor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
export interface Props {
|
||||
type?: "primary" | "neutral" | "success" | "warning" | "error" | "info";
|
||||
variant?: "soft" | "solid";
|
||||
}
|
||||
const { type = "primary", variant = "soft" } = Astro.props;
|
||||
---
|
||||
|
||||
<span class={`badge badge__${type} badge--${variant}`}>
|
||||
<slot />
|
||||
</span>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_badge.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
@include text-base;
|
||||
|
||||
&__item {
|
||||
text-decoration: none;
|
||||
color: var(--nds-neutral);
|
||||
transition: color 120ms ease;
|
||||
&:hover {
|
||||
color: var(--nds-text);
|
||||
}
|
||||
&:not(:first-child)::before {
|
||||
content: "/";
|
||||
color: var(--nds-disabled);
|
||||
margin-right: var(--nds-spacing-xs);
|
||||
}
|
||||
&--current {
|
||||
color: var(--nds-text);
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
export interface Props {}
|
||||
---
|
||||
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||||
<slot />
|
||||
</nav>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_breadcrumb.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
export interface Props {
|
||||
href?: string;
|
||||
current?: boolean;
|
||||
}
|
||||
const { href = "#", current = false } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
class={`breadcrumb__item ${current ? "breadcrumb__item--current" : ""}`}
|
||||
aria-current={current ? "page" : undefined}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_breadcrumb.scss";
|
||||
</style>
|
||||
@@ -1,40 +1,97 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
width: fit-content;
|
||||
padding: var(--nds-spacing-sm) var(--nds-spacing-md);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
@include text-base;
|
||||
border: var(--nds-border-width-thin) solid transparent;
|
||||
border-radius: var(--nds-radius-md);
|
||||
@include text-label;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
&__primary {
|
||||
background-color: var(--nds-primary);
|
||||
color: var(--nds-text);
|
||||
&:hover:not(.button--disabled) {
|
||||
background-color: var(--nds-secondary);
|
||||
user-select: none;
|
||||
transition:
|
||||
background-color 140ms ease,
|
||||
border-color 140ms ease,
|
||||
color 140ms ease,
|
||||
box-shadow 140ms ease,
|
||||
transform 80ms ease;
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px var(--nds-ring);
|
||||
}
|
||||
&:active:not(.button--disabled) {
|
||||
background-color: var(--nds-accent);
|
||||
color: var(--nds-background);
|
||||
transform: translateY(0.5px);
|
||||
}
|
||||
&--sm {
|
||||
height: 30px;
|
||||
padding: 0 var(--nds-spacing-sm);
|
||||
}
|
||||
&--md {
|
||||
height: 38px;
|
||||
padding: 0 var(--nds-spacing-md);
|
||||
}
|
||||
&--lg {
|
||||
height: 46px;
|
||||
padding: 0 var(--nds-spacing-lg);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
&--icon {
|
||||
padding: 0;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
&--icon.button--sm {
|
||||
width: 30px;
|
||||
}
|
||||
&--icon.button--md {
|
||||
width: 38px;
|
||||
}
|
||||
&--icon.button--lg {
|
||||
width: 46px;
|
||||
}
|
||||
&__primary {
|
||||
background-color: var(--nds-primary);
|
||||
color: var(--nds-on-primary);
|
||||
border-color: var(--nds-primary);
|
||||
&:hover:not(.button--disabled) {
|
||||
background-color: color-mix(in srgb, var(--nds-primary) 88%, #000);
|
||||
border-color: color-mix(in srgb, var(--nds-primary) 88%, #000);
|
||||
}
|
||||
}
|
||||
&__secondary {
|
||||
border: var(--nds-border-width-medium) solid;
|
||||
border-color: var(--nds-primary);
|
||||
background-color: transparent;
|
||||
color: var(--nds-primary);
|
||||
border-color: var(--nds-border-strong);
|
||||
&:hover:not(.button--disabled) {
|
||||
border-color: var(--nds-secondary);
|
||||
border-color: var(--nds-primary);
|
||||
background-color: var(--nds-primary-soft);
|
||||
}
|
||||
&:active:not(.button--disabled) {
|
||||
border-color: var(--nds-accent);
|
||||
color: var(--nds-accent);
|
||||
}
|
||||
&__ghost {
|
||||
background-color: transparent;
|
||||
color: var(--nds-text);
|
||||
border-color: transparent;
|
||||
&:hover:not(.button--disabled) {
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
}
|
||||
&__danger {
|
||||
background-color: var(--nds-error-medium);
|
||||
color: #fff;
|
||||
border-color: var(--nds-error-medium);
|
||||
&:hover:not(.button--disabled) {
|
||||
background-color: color-mix(in srgb, var(--nds-error-medium) 88%, #000);
|
||||
border-color: color-mix(in srgb, var(--nds-error-medium) 88%, #000);
|
||||
}
|
||||
}
|
||||
&--disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: var(--nds-disabled);
|
||||
color: var(--nds-neutral);
|
||||
}
|
||||
&--icon {
|
||||
padding: var(--nds-spacing-2xs) var(--nds-spacing-xs);
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,47 @@
|
||||
---
|
||||
export interface Props {
|
||||
type: 'primary' | 'secondary';
|
||||
type?: "primary" | "secondary" | "ghost" | "danger";
|
||||
size?: "sm" | "md" | "lg";
|
||||
disabled?: boolean;
|
||||
icon?: boolean;
|
||||
href?: string;
|
||||
htmlType?: "button" | "submit" | "reset";
|
||||
}
|
||||
const { type, disabled = false, icon = false } = Astro.props;
|
||||
const {
|
||||
type = "primary",
|
||||
size = "md",
|
||||
disabled = false,
|
||||
icon = false,
|
||||
href,
|
||||
htmlType = "button",
|
||||
...rest
|
||||
} = Astro.props;
|
||||
|
||||
const classes = [
|
||||
"button",
|
||||
`button__${type}`,
|
||||
`button--${size}`,
|
||||
icon ? "button--icon" : "",
|
||||
disabled ? "button--disabled" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
const Tag = href ? "a" : "button";
|
||||
---
|
||||
|
||||
<div class={`button button__${type} ${disabled ? 'button--disabled' : ''} ${icon ? 'button--icon' : ''}`}>
|
||||
<slot/>
|
||||
</div>
|
||||
<Tag
|
||||
class={classes}
|
||||
href={href}
|
||||
type={href ? undefined : htmlType}
|
||||
aria-disabled={disabled ? "true" : undefined}
|
||||
{...rest}
|
||||
>
|
||||
<slot name="icon-left" />
|
||||
<slot />
|
||||
<slot name="icon-right" />
|
||||
</Tag>
|
||||
|
||||
<style lang="scss">
|
||||
@use './_button.scss';
|
||||
@use "./_button.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--nds-surface);
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
border-radius: var(--nds-radius-lg);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--nds-shadow-sm);
|
||||
width: fit-content;
|
||||
transition:
|
||||
box-shadow 160ms ease,
|
||||
border-color 160ms ease;
|
||||
&:hover {
|
||||
box-shadow: var(--nds-shadow-md);
|
||||
}
|
||||
|
||||
&__cover {
|
||||
height: 150px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-md);
|
||||
}
|
||||
&__title {
|
||||
@include text-xl;
|
||||
color: var(--nds-text);
|
||||
margin: 0;
|
||||
}
|
||||
&__subtitle {
|
||||
@include text-base;
|
||||
color: var(--nds-neutral);
|
||||
margin: 0;
|
||||
}
|
||||
&__content {
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&__footer {
|
||||
margin-top: var(--nds-spacing-2xs);
|
||||
display: flex;
|
||||
gap: var(--nds-spacing-xs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
cover?: string;
|
||||
}
|
||||
const { title, subtitle, cover } = Astro.props;
|
||||
const hasFooter = Astro.slots.has("footer");
|
||||
---
|
||||
|
||||
<div class="card">
|
||||
{
|
||||
cover && (
|
||||
<div class="card__cover" style={`background-image: url(${cover})`} />
|
||||
)
|
||||
}
|
||||
<div class="card__body">
|
||||
{title && <p class="card__title">{title}</p>}
|
||||
{subtitle && <p class="card__subtitle">{subtitle}</p>}
|
||||
<div class="card__content"><slot /></div>
|
||||
{
|
||||
hasFooter && (
|
||||
<div class="card__footer">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_card.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.checkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
cursor: pointer;
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
&__input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
&__box {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: var(--nds-border-width-medium) solid var(--nds-border-strong);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
background-color: var(--nds-surface);
|
||||
color: var(--nds-on-primary);
|
||||
transition: all 130ms ease;
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
transition: all 130ms ease;
|
||||
}
|
||||
}
|
||||
&__input:checked + &__box {
|
||||
background-color: var(--nds-primary);
|
||||
border-color: var(--nds-primary);
|
||||
svg {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&__input:focus-visible + &__box {
|
||||
box-shadow: 0 0 0 3px var(--nds-ring);
|
||||
}
|
||||
&--disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
export interface Props {
|
||||
id: string;
|
||||
name?: string;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
const { id, name, checked = false, disabled = false } = Astro.props;
|
||||
---
|
||||
|
||||
<label class={`checkbox ${disabled ? "checkbox--disabled" : ""}`} for={id}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={id}
|
||||
name={name}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
class="checkbox__input"
|
||||
/>
|
||||
<span class="checkbox__box" aria-hidden="true">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="checkbox__label"><slot /></span>
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_checkbox.scss";
|
||||
</style>
|
||||
@@ -1,30 +1,28 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.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;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: var(--nds-border-width-medium) dashed var(--nds-secondary);
|
||||
gap: var(--nds-spacing-3xs);
|
||||
border-bottom: 1px solid transparent;
|
||||
transition:
|
||||
border-color 130ms ease,
|
||||
color 130ms ease;
|
||||
&: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);
|
||||
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,70 +1,31 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.list-item {
|
||||
width: 350px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--nds-spacing-md);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
background: var(--nds-background, #040B0B);
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: color-mix(in srgb, var(--nds-secondary) 25%, transparent);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: color-mix(in srgb, var(--nds-accent) 25%, transparent);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-right: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
gap: var(--nds-spacing-sm);
|
||||
padding: var(--nds-spacing-sm) var(--nds-spacing-md);
|
||||
border-radius: var(--nds-radius-md);
|
||||
border-color: transparent;
|
||||
background:
|
||||
linear-gradient(var(--nds-background, #040B0B), var(--nds-background, #040B0B)) padding-box,
|
||||
linear-gradient(
|
||||
135deg,
|
||||
transparent 0%,
|
||||
transparent 20%,
|
||||
rgba(245, 250, 250, 0.08) 50%,
|
||||
rgba(245, 250, 250, 0.28) 100%
|
||||
) border-box;
|
||||
-webkit-mask:
|
||||
linear-gradient(#fff 0 0) padding-box,
|
||||
linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: destination-out;
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
transition: background-color 120ms ease;
|
||||
&:hover {
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1px;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&__content {
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--nds-spacing-md);
|
||||
gap: var(--nds-spacing-3xs);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__subTitle {
|
||||
@include text-base;
|
||||
}
|
||||
|
||||
&__title {
|
||||
@include text-xl;
|
||||
@include text-label;
|
||||
color: var(--nds-text);
|
||||
margin: 0;
|
||||
}
|
||||
&__subtitle {
|
||||
@include text-sm;
|
||||
color: var(--nds-neutral);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
---
|
||||
import Arrow2 from "../Icons/Arrow2/Arrow2.astro"
|
||||
export interface Props {}
|
||||
---
|
||||
<div class='list-item'>
|
||||
<div class='list-item__content'>
|
||||
<slot/>
|
||||
</div>
|
||||
<Arrow2
|
||||
size={24}
|
||||
orientation='right' />
|
||||
|
||||
<div class="list-item">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use './listItem';
|
||||
@use "./_listItem.scss";
|
||||
</style>
|
||||
@@ -1,7 +1,11 @@
|
||||
<div class='list-item__subTitle'>
|
||||
<slot/>
|
||||
</div>
|
||||
---
|
||||
export interface Props {}
|
||||
---
|
||||
|
||||
<p class="list-item__subtitle">
|
||||
<slot />
|
||||
</p>
|
||||
|
||||
<style lang="scss">
|
||||
@use './listItem';
|
||||
@use "./_listItem.scss";
|
||||
</style>
|
||||
@@ -1,7 +1,11 @@
|
||||
<div class='list-item__title'>
|
||||
<slot/>
|
||||
</div>
|
||||
---
|
||||
export interface Props {}
|
||||
---
|
||||
|
||||
<p class="list-item__title">
|
||||
<slot />
|
||||
</p>
|
||||
|
||||
<style lang="scss">
|
||||
@use './listItem';
|
||||
@use "./_listItem.scss";
|
||||
</style>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@keyframes loadingBarIndeterminate {
|
||||
from {
|
||||
left: -50%;
|
||||
}
|
||||
to {
|
||||
left: 100%;
|
||||
}
|
||||
border-radius: var(--nds-radius-full);
|
||||
animation: nds-lb 1.3s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
@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;
|
||||
}
|
||||
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 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>
|
||||
)}
|
||||
<div class="loading-bar__unknown" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@use './loadingBar';
|
||||
@use "./_loadingBar.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.modal {
|
||||
&__overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(2, 12, 12, 0.55);
|
||||
backdrop-filter: blur(3px);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 200;
|
||||
padding: var(--nds-spacing-md);
|
||||
}
|
||||
&__dialog {
|
||||
width: min(460px, 100%);
|
||||
background-color: var(--nds-surface);
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
border-radius: var(--nds-radius-xl);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--nds-shadow-lg);
|
||||
animation: nds-modal-in 180ms cubic-bezier(0.2, 0.8, 0.3, 1);
|
||||
}
|
||||
&__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--nds-spacing-md) var(--nds-spacing-lg);
|
||||
border-bottom: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
}
|
||||
&__title {
|
||||
@include text-xl;
|
||||
color: var(--nds-text);
|
||||
}
|
||||
&__close {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--nds-neutral);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: var(--nds-spacing-3xs);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
transition:
|
||||
color 120ms ease,
|
||||
background-color 120ms ease;
|
||||
&:hover {
|
||||
color: var(--nds-text);
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
}
|
||||
&__body {
|
||||
padding: var(--nds-spacing-lg);
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
}
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-md) var(--nds-spacing-lg);
|
||||
border-top: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
}
|
||||
}
|
||||
|
||||
nds-modal[data-open="true"] .modal__overlay {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes nds-modal-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px) scale(0.98);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
import { CloseIcon } from "../Icons";
|
||||
|
||||
export interface Props {
|
||||
id: string;
|
||||
title?: string;
|
||||
open?: boolean;
|
||||
}
|
||||
const { id, title, open = false } = Astro.props;
|
||||
const hasFooter = Astro.slots.has("footer");
|
||||
---
|
||||
|
||||
<nds-modal id={id} class="modal" data-open={open ? "true" : undefined}>
|
||||
<div class="modal__overlay" data-modal-close>
|
||||
<div
|
||||
class="modal__dialog"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={title}
|
||||
>
|
||||
<div class="modal__head">
|
||||
<span class="modal__title">{title}</span>
|
||||
<button class="modal__close" data-modal-close aria-label="Close">
|
||||
<CloseIcon size={18} label="Close" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal__body"><slot /></div>
|
||||
{
|
||||
hasFooter && (
|
||||
<div class="modal__footer">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</nds-modal>
|
||||
|
||||
<script>
|
||||
class NdsModal extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.addEventListener("click", (e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (
|
||||
target.closest("[data-modal-close]") ===
|
||||
target.closest(".modal__dialog")
|
||||
) {
|
||||
// clicked dialog interior, ignore
|
||||
}
|
||||
if (
|
||||
target.matches("[data-modal-close]") ||
|
||||
target.classList.contains("modal__overlay")
|
||||
) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && this.dataset.open === "true") this.close();
|
||||
});
|
||||
}
|
||||
open() {
|
||||
this.dataset.open = "true";
|
||||
this.dispatchEvent(new CustomEvent("nds:open", { bubbles: true }));
|
||||
}
|
||||
close() {
|
||||
delete this.dataset.open;
|
||||
this.dispatchEvent(new CustomEvent("nds:close", { bubbles: true }));
|
||||
}
|
||||
}
|
||||
customElements.define("nds-modal", NdsModal);
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
const trigger = (e.target as HTMLElement).closest<HTMLElement>(
|
||||
"[data-modal-open]",
|
||||
);
|
||||
if (!trigger) return;
|
||||
const modal = document.getElementById(trigger.dataset.modalOpen!) as any;
|
||||
modal?.open?.();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_modal.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-lg);
|
||||
height: 60px;
|
||||
padding: 0 var(--nds-spacing-lg);
|
||||
background-color: var(--nds-surface);
|
||||
border-bottom: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
|
||||
&__brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
}
|
||||
&__mark {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: var(--nds-radius-md);
|
||||
background: linear-gradient(135deg, var(--nds-primary), var(--nds-accent));
|
||||
}
|
||||
&__title {
|
||||
@include text-xl;
|
||||
font-size: 1rem;
|
||||
color: var(--nds-text);
|
||||
}
|
||||
|
||||
&__links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
flex: 1;
|
||||
|
||||
:global(.navbar__link) {
|
||||
padding: var(--nds-spacing-2xs) var(--nds-spacing-sm);
|
||||
border-radius: var(--nds-radius-md);
|
||||
@include text-label;
|
||||
color: var(--nds-text);
|
||||
text-decoration: none !important;
|
||||
transition: all 120ms ease;
|
||||
|
||||
}
|
||||
:global(.navbar__link:hover) {
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
|
||||
// On cible la variante active globale séparément
|
||||
:global(.navbar__link--active) {
|
||||
color: var(--nds-primary);
|
||||
}
|
||||
}
|
||||
&__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-sm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
export interface Props {
|
||||
brand?: string;
|
||||
}
|
||||
const { brand = "Nova" } = Astro.props;
|
||||
---
|
||||
|
||||
<header class="navbar">
|
||||
<div class="navbar__brand">
|
||||
<span class="navbar__mark"></span>
|
||||
<span class="navbar__title">{brand}</span>
|
||||
</div>
|
||||
<nav class="navbar__links"><slot /></nav>
|
||||
<div class="navbar__right"><slot name="right" /></div>
|
||||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_navbar.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 {
|
||||
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;
|
||||
color: var(--nds-info-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
color: var(--nds-info-high);
|
||||
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';
|
||||
@use "./_notification.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.radio {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
cursor: pointer;
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
&__input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
&__dot {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: var(--nds-border-width-medium) solid var(--nds-border-strong);
|
||||
border-radius: var(--nds-radius-full);
|
||||
background-color: var(--nds-surface);
|
||||
transition: border-color 130ms ease;
|
||||
&::after {
|
||||
content: "";
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: var(--nds-radius-full);
|
||||
background-color: var(--nds-primary);
|
||||
transform: scale(0);
|
||||
transition: transform 130ms cubic-bezier(0.34, 1.4, 0.6, 1);
|
||||
}
|
||||
}
|
||||
&__input:checked + &__dot {
|
||||
border-color: var(--nds-primary);
|
||||
&::after {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&__input:focus-visible + &__dot {
|
||||
box-shadow: 0 0 0 3px var(--nds-ring);
|
||||
}
|
||||
&--disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
export interface Props {
|
||||
id: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
const { id, name, value, checked = false, disabled = false } = Astro.props;
|
||||
---
|
||||
|
||||
<label class={`radio ${disabled ? "radio--disabled" : ""}`} for={id}>
|
||||
<input
|
||||
type="radio"
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
class="radio__input"
|
||||
/>
|
||||
<span class="radio__dot" aria-hidden="true"></span>
|
||||
<span class="radio__label"><slot /></span>
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_radio.scss";
|
||||
</style>
|
||||
@@ -1,71 +1,90 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-width: 200px;
|
||||
|
||||
&__trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-md);
|
||||
background-color: var(--nds-neutral);
|
||||
color: var(--nds-text);
|
||||
border: none;
|
||||
border-radius: var(--nds-radius-sm);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
gap: var(--nds-spacing-xs);
|
||||
|
||||
height: 38px;
|
||||
padding: 0 var(--nds-spacing-sm);
|
||||
background-color: var(--nds-surface);
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border-strong);
|
||||
border-radius: var(--nds-radius-md);
|
||||
cursor: pointer;
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
transition:
|
||||
border-color 130ms ease,
|
||||
box-shadow 130ms ease;
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--nds-neutral) 85%, var(--nds-text));
|
||||
border-color: var(--nds-neutral);
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
border-color: var(--nds-primary);
|
||||
box-shadow: 0 0 0 3px var(--nds-ring);
|
||||
}
|
||||
|
||||
&__label {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
svg {
|
||||
color: var(--nds-neutral);
|
||||
transition: transform 160ms ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
&__placeholder {
|
||||
color: var(--nds-neutral);
|
||||
}
|
||||
|
||||
&[data-open="true"] &__trigger {
|
||||
border-color: var(--nds-primary);
|
||||
}
|
||||
&[data-open="true"] &__trigger svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&__menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 40;
|
||||
background-color: var(--nds-surface);
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
border-radius: var(--nds-radius-md);
|
||||
box-shadow: var(--nds-shadow-md);
|
||||
padding: var(--nds-spacing-3xs);
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
}
|
||||
&__arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + var(--nds-spacing-2xs));
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--nds-neutral);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
.select__trigger {
|
||||
background-color: var(--nds-disabled);
|
||||
cursor: not-allowed;
|
||||
background-color: var(--nds-surface);
|
||||
color: var(--nds-neutral);
|
||||
border-color: var(--nds-border-strong);
|
||||
opacity: 0.55;
|
||||
& .select__trigger {
|
||||
cursor: not-allowed;
|
||||
&:hover {
|
||||
border-color: var(--nds-border-strong);
|
||||
}
|
||||
}
|
||||
}
|
||||
&[data-open="true"] &__menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,28 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.select-option {
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-sm);
|
||||
cursor: pointer;
|
||||
border-radius: var(--nds-radius-sm);
|
||||
@include text-base;
|
||||
color: var(--nds-text);
|
||||
background-color: var(--nds-neutral);
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
&:hover:not(.select-option__disabled) {
|
||||
background-color: var(--nds-secondary);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--nds-accent);
|
||||
}
|
||||
|
||||
&__selected {
|
||||
background-color: var(--nds-primary);
|
||||
color: var(--nds-text);
|
||||
transition: background-color 110ms ease;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--nds-primary) 85%, var(--nds-background));
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
&[aria-selected="true"] {
|
||||
color: var(--nds-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__disabled {
|
||||
background-color: var(--nds-disabled);
|
||||
color: color-mix(in srgb, var(--nds-text) 40%, transparent);
|
||||
color: var(--nds-disabled);
|
||||
cursor: not-allowed;
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 240px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: var(--nds-spacing-sm);
|
||||
background-color: var(--nds-surface);
|
||||
border-right: var(--nds-border-width-thin) solid var(--nds-border);
|
||||
|
||||
&__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-xs) var(--nds-spacing-md);
|
||||
}
|
||||
&__mark {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: var(--nds-radius-md);
|
||||
background: linear-gradient(135deg, var(--nds-primary), var(--nds-accent));
|
||||
}
|
||||
&__title {
|
||||
@include text-xl;
|
||||
font-size: 1rem;
|
||||
color: var(--nds-text);
|
||||
}
|
||||
|
||||
&__nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--nds-spacing-3xs);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-sm);
|
||||
border-radius: var(--nds-radius-md);
|
||||
text-decoration: none;
|
||||
color: var(--nds-text);
|
||||
@include text-label;
|
||||
font-weight: 500;
|
||||
transition:
|
||||
background-color 120ms ease,
|
||||
color 120ms ease;
|
||||
&:hover {
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
&--active {
|
||||
background-color: var(--nds-primary-soft);
|
||||
color: var(--nds-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
&__item-icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&__footer {
|
||||
padding-top: var(--nds-spacing-sm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
export interface Props {
|
||||
header?: string;
|
||||
}
|
||||
const { header = "Nova" } = Astro.props;
|
||||
---
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar__head">
|
||||
<span class="sidebar__mark"></span>
|
||||
<span class="sidebar__title">{header}</span>
|
||||
</div>
|
||||
<nav class="sidebar__nav">
|
||||
<slot />
|
||||
</nav>
|
||||
<div class="sidebar__footer">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_sidebar.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
export interface Props {
|
||||
href?: string;
|
||||
active?: boolean;
|
||||
}
|
||||
const { href = "#", active = false } = Astro.props;
|
||||
const hasIcon = Astro.slots.has("icon");
|
||||
---
|
||||
|
||||
<a href={href} class={`sidebar__item ${active ? "sidebar__item--active" : ""}`}>
|
||||
{
|
||||
hasIcon && (
|
||||
<span class="sidebar__item-icon">
|
||||
<slot name="icon" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
<span class="sidebar__item-label"><slot /></span>
|
||||
</a>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_sidebar.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;
|
||||
align-items: stretch;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
border-bottom: 1px solid var(--nds-border);
|
||||
width: fit-content;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: fit-content;
|
||||
gap: var(--nds-spacing-2xs);
|
||||
padding: var(--nds-spacing-xs) var(--nds-spacing-sm);
|
||||
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);
|
||||
@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>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
&__bubble {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
padding: var(--nds-spacing-2xs) var(--nds-spacing-xs);
|
||||
background-color: var(--nds-text);
|
||||
color: var(--nds-background);
|
||||
border-radius: var(--nds-radius-sm);
|
||||
@include text-sm;
|
||||
font-weight: 500;
|
||||
box-shadow: var(--nds-shadow-md);
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 130ms ease;
|
||||
}
|
||||
&:hover &__bubble,
|
||||
&:focus-within &__bubble {
|
||||
opacity: 1;
|
||||
}
|
||||
&--top &__bubble {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -6px);
|
||||
}
|
||||
&--bottom &__bubble {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 6px);
|
||||
}
|
||||
&--left &__bubble {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
transform: translate(-6px, -50%);
|
||||
}
|
||||
&--right &__bubble {
|
||||
left: 100%;
|
||||
top: 50%;
|
||||
transform: translate(6px, -50%);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
export interface Props {
|
||||
label: string;
|
||||
position?: "top" | "bottom" | "left" | "right";
|
||||
}
|
||||
const { label, position = "top" } = Astro.props;
|
||||
---
|
||||
|
||||
<span class={`tooltip tooltip--${position}`}>
|
||||
<slot />
|
||||
<span class="tooltip__bubble" role="tooltip">{label}</span>
|
||||
</span>
|
||||
|
||||
<style lang="scss">
|
||||
@use "./_tooltip.scss";
|
||||
</style>
|
||||
@@ -1,14 +1,44 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.numeric-stepper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--nds-spacing-xs);
|
||||
gap: 0;
|
||||
border: var(--nds-border-width-thin) solid var(--nds-border-strong);
|
||||
border-radius: var(--nds-radius-md);
|
||||
background-color: var(--nds-surface);
|
||||
overflow: hidden;
|
||||
width: fit-content;
|
||||
& p {
|
||||
@include text-xl;
|
||||
margin: 0;
|
||||
height: fit-content;
|
||||
|
||||
&__button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--nds-text);
|
||||
transition: background-color 120ms ease;
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--nds-surface-hover);
|
||||
}
|
||||
&:disabled {
|
||||
color: var(--nds-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
&__value {
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
@include text-label;
|
||||
color: var(--nds-text);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import Button from '../Button/button.astro';
|
||||
import { Arrow2Icon } from '../Icons';
|
||||
import { MinusIcon, PlusIcon } from '../Icons';
|
||||
|
||||
export interface Props {
|
||||
id: string;
|
||||
@@ -23,16 +23,16 @@ const { id, min, max, value = 0, step = 1 } = Astro.props;
|
||||
data-step={step}
|
||||
>
|
||||
<span class="numeric-stepper__control" data-action="increment">
|
||||
<Button type="secondary" icon={true}>
|
||||
<Arrow2Icon size={24} orientation="up" />
|
||||
<Button type="ghost" icon={true}>
|
||||
<MinusIcon size={24} />
|
||||
</Button>
|
||||
</span>
|
||||
|
||||
<p class="numeric-stepper__value">{value}</p>
|
||||
|
||||
<span class="numeric-stepper__control" data-action="decrement">
|
||||
<Button type="secondary" icon={true}>
|
||||
<Arrow2Icon size={24} orientation="down" />
|
||||
<Button type="ghost" icon={true}>
|
||||
<PlusIcon size={24} />
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -113,5 +113,5 @@ const { id, min, max, value = 0, step = 1 } = Astro.props;
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use './numericStepper';
|
||||
@use "./numericStepper";
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
@use '../../styles/tokens/typography' as *;
|
||||
@use "../../styles/tokens/typography" as *;
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
gap: var(--nds-spacing-sm);
|
||||
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 {
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
---
|
||||
import '../styles/index.scss';
|
||||
interface Props { title?: string; }
|
||||
const { title = 'Nova Design System' } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Nova Design System</title>
|
||||
<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
+672
-348
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user