svg]:size-3.5",
- className
- )}
+ className={cn(variants[variant], "rustlanges-chip", className)}
{...attr}
>
{icons[variant] ? React.createElement(icons[variant]) : null}
diff --git a/js/react/lib/components/chip/chip.const.ts b/js/react/lib/components/chip/chip.const.ts
index 7312437..33aaaee 100644
--- a/js/react/lib/components/chip/chip.const.ts
+++ b/js/react/lib/components/chip/chip.const.ts
@@ -1,41 +1,11 @@
import { Location, StarBold } from "@/icons";
export const variants = {
- featured: [
- "border rounded-[20px]",
- "bg-primary-400 border-black text-black",
- "min-h-[32px]",
- "text-sm",
- "p-[4px] px-[12px]",
- ],
- numeric: [
- "border rounded-[20px]",
- "bg-primary-200 border-black text-black",
- "min-h-[32px]",
- "text-sm",
- "p-[4px] px-[12px]",
- ],
- description: [
- "border rounded-[20px]",
- "bg-secondary-200 border-black text-black",
- "min-h-[32px]",
- "text-sm",
- "p-[4px] px-[12px]",
- ],
- location: [
- "border rounded-[20px]",
- "bg-secondary-200 border-black text-black",
- "min-h-[24px]",
- "text-sm",
- "p-[2px] px-[12px]",
- ],
- small: [
- "border rounded-[20px]",
- "bg-secondary-200 border-black text-black",
- "min-h-[22px]",
- "text-xs",
- "p-[2px] px-[8px]",
- ],
+ featured: "rustlanges-chip--featured",
+ numeric: "rustlanges-chip--numeric",
+ description: "rustlanges-chip--description",
+ location: "rustlanges-chip--location",
+ small: "rustlanges-chip--small",
};
export type ChipVariants = keyof typeof variants;
diff --git a/js/react/lib/components/collaborators/collaborators.component.tsx b/js/react/lib/components/collaborators/collaborators.component.tsx
index 7594d87..ceaf5ba 100644
--- a/js/react/lib/components/collaborators/collaborators.component.tsx
+++ b/js/react/lib/components/collaborators/collaborators.component.tsx
@@ -24,8 +24,8 @@ export const Collaborators = ({
const hasMaxCollaborators = numberOfCollaborators > MAX_COLLABORATORS;
const extraCollaborators = numberOfCollaborators - MAX_COLLABORATORS;
return (
-
-
+
+
{collaborators.slice(0, MAX_COLLABORATORS).map((collaborator, idx) => {
const space = idx ? 12 : 0;
return (
diff --git a/js/react/lib/components/dropdown/dropdown.component.tsx b/js/react/lib/components/dropdown/dropdown.component.tsx
index 4581629..d213dbf 100644
--- a/js/react/lib/components/dropdown/dropdown.component.tsx
+++ b/js/react/lib/components/dropdown/dropdown.component.tsx
@@ -23,37 +23,32 @@ export const DropdownState = ({ value, onChange }: DropdownStateProps) => {
const Icon = open ? ArrowUp : ArrowDown;
return (
-
+
{open && (
-
+
{DROPDOWN_OPTIONS.map(opt => (
- handleSelect(opt.value)}
>
diff --git a/js/react/lib/components/flap/flap.component.tsx b/js/react/lib/components/flap/flap.component.tsx
index e413835..bb6237f 100644
--- a/js/react/lib/components/flap/flap.component.tsx
+++ b/js/react/lib/components/flap/flap.component.tsx
@@ -10,24 +10,12 @@ type FlapProps = {
export const Flap = ({ label, variant, className, ...rest }: FlapProps) => {
const Icon = FLAP_ICONS[variant];
return (
-
+
svg]:h-3 [&>svg]:w-3",
+ "rustlanges-flap__view",
+ !!Icon && "rustlanges-flap__view--icon",
])}
>
{Icon ? : null}
-
+
{label}
diff --git a/js/react/lib/components/flap/flap.const.ts b/js/react/lib/components/flap/flap.const.ts
index f3866e3..126b14f 100644
--- a/js/react/lib/components/flap/flap.const.ts
+++ b/js/react/lib/components/flap/flap.const.ts
@@ -1,9 +1,9 @@
import { StarBold } from "@/icons";
export const FLAP_VARIANTS = {
- highlight: "text-primary-400",
- numeric: "text-primary-200",
- descriptive: "text-secondary-400",
+ highlight: "rustlanges-flap--highlight",
+ numeric: "rustlanges-flap--numeric",
+ descriptive: "rustlanges-flap--descriptive",
};
export const FLAP_ICONS = {
diff --git a/js/react/lib/components/level/level.component.tsx b/js/react/lib/components/level/level.component.tsx
index c63a333..06897c3 100644
--- a/js/react/lib/components/level/level.component.tsx
+++ b/js/react/lib/components/level/level.component.tsx
@@ -12,12 +12,7 @@ export const Level = withAs(
return (
{LEVEL_LABELS[variant]}
diff --git a/js/react/lib/components/level/level.const.ts b/js/react/lib/components/level/level.const.ts
index 92b6023..b28e691 100644
--- a/js/react/lib/components/level/level.const.ts
+++ b/js/react/lib/components/level/level.const.ts
@@ -1,8 +1,8 @@
export const LEVEL_VARIANTS = {
- n1: "bg-primary-100",
- n2: "bg-primary-300",
- n3: "bg-primary-500",
- op: "bg-secondary-400",
+ n1: "rustlanges-level--n1",
+ n2: "rustlanges-level--n2",
+ n3: "rustlanges-level--n3",
+ op: "rustlanges-level--op",
};
export const LEVEL_LABELS = {
diff --git a/js/react/lib/components/radio/radio.component.tsx b/js/react/lib/components/radio/radio.component.tsx
index 69fb219..60af7b4 100644
--- a/js/react/lib/components/radio/radio.component.tsx
+++ b/js/react/lib/components/radio/radio.component.tsx
@@ -6,15 +6,7 @@ type RadioProps = InputHTMLAttributes;
export const Radio = ({ className, ...rest }: RadioProps) => {
return (
diff --git a/js/react/lib/components/tag/tag.component.tsx b/js/react/lib/components/tag/tag.component.tsx
index 5639f4c..8c5be97 100644
--- a/js/react/lib/components/tag/tag.component.tsx
+++ b/js/react/lib/components/tag/tag.component.tsx
@@ -1,6 +1,5 @@
import { withAs } from "@/utils/hoc";
import { cn } from "@/utils/tw-merge";
-import { TAG_VARIANTS } from "./tag.const";
type TagProps = {
label?: string;
@@ -13,8 +12,8 @@ export const Tag = withAs(
return (
= theme(--breakpoint-sm)) {
- padding-left: calc((100vw + 16px - 640px) / 2);
- padding-right: calc((100vw + 16px - 640px) / 2);
- }
-
- @media (width >= theme(--breakpoint-md)) {
- padding-left: calc((100vw + 16px - 768px) / 2);
- padding-right: calc((100vw + 16px - 768px) / 2);
- }
-
- @media (width >= theme(--breakpoint-lg)) {
- padding-left: calc((100vw + 16px - 1024px) / 2);
- padding-right: calc((100vw + 16px - 1024px) / 2);
- }
-
- @media (width >= theme(--breakpoint-xl)) {
- padding-left: calc((100vw + 16px - 1280px) / 2);
- padding-right: calc((100vw + 16px - 1280px) / 2);
- }
-
- @media (width >= theme(--breakpoint-2xl)) {
- padding-left: calc((100vw + 16px - 1536px) / 2);
- padding-right: calc((100vw + 16px - 1536px) / 2);
- }
-}
-
-@custom-variant mobile (@media (max-width: 767px));
-
-@layer components {
- .text-h1 {
- font-size: 32px;
- font-weight: 700;
- line-height: 125%;
- letter-spacing: 0.32%;
-
- @apply desktop:text-[48px];
- }
-
- .text-h2 {
- font-size: 32px;
- font-weight: 700;
- line-height: 140%;
- letter-spacing: 0.24%;
-
- @apply desktop:text-[32px];
- }
-
- .text-h3 {
- font-size: 24px;
- font-weight: 600;
- line-height: 150%;
- letter-spacing: 0.2%;
-
- @apply desktop:text-[28px];
- }
-
- .text-h4 {
- font-size: 18px;
- font-weight: 600;
- line-height: 160%;
- letter-spacing: 0.18%;
-
- @apply desktop:text-[24px];
- }
-
- .text-h5 {
- font-size: 16px;
- font-weight: 500;
- line-height: 160%;
- letter-spacing: 0.08%;
-
- @apply desktop:text-[20px];
- }
-
- .text-h6 {
- font-size: 14px;
- font-weight: 500;
- line-height: 160%;
- letter-spacing: 0.07%;
-
- @apply desktop:text-[18px];
- }
-
- .text-subtitle-1 {
- font-size: 16px;
- line-height: 175%;
- letter-spacing: 0.08%;
-
- @apply desktop:text-[20px];
- }
-
- .text-subtitle-2 {
- font-size: 14px;
- font-weight: 500;
- line-height: 160%;
- letter-spacing: 0.08%;
-
- @apply desktop:text-[16px];
- }
-
- .text-paragraph-1 {
- font-size: 14px;
- line-height: 160%;
- letter-spacing: 0;
-
- @apply desktop:text-[16px];
- }
-
- .text-paragraph-2 {
- font-size: 12px;
- line-height: 150%;
- letter-spacing: 0;
-
- @apply desktop:text-[14px];
- }
-
- .text-button {
- font-size: 14px;
- font-weight: 600;
- line-height: 160%;
- letter-spacing: 0.28%;
- @apply desktop:text-[16px];
- }
-
- .text-caption {
- font-size: 12px;
- line-height: 150%;
- letter-spacing: 0.06%;
- }
-
- .text-overline {
- font-size: 10px;
- font-weight: 600;
- line-height: 150%;
- letter-spacing: 1%;
- }
-
- .heading {
- font-size: 28px;
- font-weight: 700;
- line-height: 125%;
- letter-spacing: 0.32%;
-
- @apply desktop:text-[40px];
- }
-}
-
-@layer utilities {
- @supports not selector(::-webkit-scrollbar) {
- .scrollbar {
- scrollbar-color: var(--color-neutral-300) var(--color-neutral-100);
-
- @variant dark {
- scrollbar-color: var(--color-neutral-900) var(--color-neutral-600);
- }
- }
- }
-
- .scrollbar::-webkit-scrollbar {
- @apply w-4;
- }
-
- .scrollbar::-webkit-scrollbar-track {
- @apply rounded-full bg-neutral-100 dark:bg-neutral-900;
- }
-
- .scrollbar::-webkit-scrollbar-thumb {
- @apply rounded-full bg-neutral-300 transition dark:bg-neutral-600;
- }
- .scrollbar::-webkit-scrollbar-thumb:hover {
- @apply opacity-90;
- }
-}
+@import "@rustlanges/styles";
diff --git a/js/react/package.json b/js/react/package.json
index 76a1571..4decec3 100644
--- a/js/react/package.json
+++ b/js/react/package.json
@@ -34,6 +34,7 @@
},
"devDependencies": {
"@eslint/js": "^9.28.0",
+ "@rustlanges/styles": "file:../../styles",
"@tailwindcss/cli": "^4.1.8",
"@tailwindcss/vite": "^4.1.8",
"@types/node": "^22.15.29",
diff --git a/js/react/tsconfig.app.json b/js/react/tsconfig.app.json
index 7e00afd..83f561d 100644
--- a/js/react/tsconfig.app.json
+++ b/js/react/tsconfig.app.json
@@ -20,7 +20,7 @@
// Path aliases
"baseUrl": "./lib",
"paths": {
- "@/*": ["./*"],
+ "@/*": ["./*"]
},
/* Linting */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e8cbf4e..5fa8586 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -27,6 +27,9 @@ importers:
'@eslint/js':
specifier: ^9.28.0
version: 9.28.0
+ '@rustlanges/styles':
+ specifier: file:../../styles
+ version: link:../../styles
'@tailwindcss/cli':
specifier: ^4.1.8
version: 4.1.8
@@ -88,6 +91,15 @@ importers:
specifier: ^4.5.4
version: 4.5.4(@types/node@22.15.29)(rollup@4.41.1)(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1))
+ styles:
+ devDependencies:
+ '@tailwindcss/cli':
+ specifier: ^4.1.8
+ version: 4.1.8
+ tailwindcss:
+ specifier: ^4.1.8
+ version: 4.1.8
+
packages:
'@ampproject/remapping@2.3.0':
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index bc681d9..fc52176 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,5 +1,6 @@
packages:
- js/react
+ - styles
onlyBuiltDependencies:
- "@parcel/watcher"
- "@swc/core"
diff --git a/styles/.gitignore b/styles/.gitignore
new file mode 100644
index 0000000..69192d0
--- /dev/null
+++ b/styles/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+
+dist/
diff --git a/styles/README.md b/styles/README.md
new file mode 100644
index 0000000..e2af02e
--- /dev/null
+++ b/styles/README.md
@@ -0,0 +1,63 @@
+# RustLangES Design System styles
+
+### Installation
+
+> [!WARNING]
+> Both deploys are pending and subject to change.
+
+```bash
+$ npm install @rustlanges/styles
+```
+
+or by cdn
+
+```html
+
+```
+
+## Usage
+
+### Using tailwindcss
+
+In your tailwindcss's styles file import `@rustlanges/styles`, or import just
+the modules needed.
+
+
+```css
+/* For complete theme */
+@import "@rustlanges/styles";
+
+/* For modular theme */
+@import "@rustlanges/styles/theme";
+@import "@rustlanges/styles/breakpoints";
+@import "@rustlanges/styles/components";
+@import "@rustlanges/styles/utilities";
+
+@import "@rustlanges/styles/components/text";
+@import "@rustlanges/styles/utilities/shadow";
+```
+
+> [!NOTE]
+> Importing `@rustlanges/styles` also import default theme from tailwindcss
+> `tailwindcss/theme` and `tailwindcss/utilities` but don't reset styles.
+
+### Using vite:
+
+```js
+// With tailwindcss installed
+import "@rustlanges/styles";
+
+// Without tailwindcss installed
+import "@rustlanges/styles/bundled.css";
+```
+
+> [!NOTE]
+> Just import component styles and some utilities classes defined in [safelist.txt](./safelist.txt).
+
+## Project structure
+
+```
+.
+├── components/
+└── utilities/
+```
diff --git a/styles/breakpoints.css b/styles/breakpoints.css
new file mode 100644
index 0000000..f6fcc66
--- /dev/null
+++ b/styles/breakpoints.css
@@ -0,0 +1 @@
+@custom-variant mobile (@media (max-width: 767px));
diff --git a/styles/components.css b/styles/components.css
new file mode 100644
index 0000000..dc9ae68
--- /dev/null
+++ b/styles/components.css
@@ -0,0 +1,11 @@
+@import "./components/avatar.css";
+@import "./components/badge.css";
+@import "./components/button.css";
+@import "./components/chip.css";
+@import "./components/collaborators.css";
+@import "./components/dropdown.css";
+@import "./components/flap.css";
+@import "./components/level.css";
+@import "./components/radio.css";
+@import "./components/tag.css";
+@import "./components/text.css";
diff --git a/styles/components/avatar.css b/styles/components/avatar.css
new file mode 100644
index 0000000..909499a
--- /dev/null
+++ b/styles/components/avatar.css
@@ -0,0 +1,9 @@
+@layer components {
+ .rustlanges-avatar {
+ @apply grid aspect-square place-items-center overflow-hidden rounded-full border object-cover;
+ }
+
+ .rustlanges-avatar__img {
+ @apply aspect-square size-full;
+ }
+}
diff --git a/styles/components/badge.css b/styles/components/badge.css
new file mode 100644
index 0000000..5d9abd8
--- /dev/null
+++ b/styles/components/badge.css
@@ -0,0 +1,50 @@
+@layer components {
+ .rustlanges-badge {
+ @apply flex w-fit items-center gap-1 rounded-full border-[0.8px] border-black px-2;
+
+ font-size: 10px;
+ @variant desktop {
+ font-size: 12px;
+ }
+ }
+
+ .rustlanges-badge__dot {
+ @apply size-1 rounded-full;
+ }
+
+ .rustlanges-badge--type-default {
+ @apply px-0! size-4 justify-center [&>span]:hidden;
+ }
+
+ .rustlanges-badge--type-numeric {
+ @apply flex-row-reverse;
+ }
+
+ .rustlanges-badge--variant-completed {
+ @apply bg-success-100 text-success-600 [&>div]:bg-success-600;
+ @variant dark {
+ @apply bg-success-950 text-success-400 [&>div]:bg-success-600;
+ }
+ }
+
+ .rustlanges-badge--variant-reading {
+ @apply bg-warning-100 text-warning-500 [&>div]:bg-warning-500;
+ @variant dark {
+ @apply bg-warning-950 text-warning-300 [&>div]:bg-warning-300;
+ }
+ }
+
+ .rustlanges-badge--variant-pending {
+ @apply bg-error-100 text-error-600 [&>div]:bg-error-600;
+ @variant dark {
+ @apply bg-error-950 text-error-300 [&>div]:bg-error-300;
+ }
+ }
+
+ .rustlanges-badge--variant-unread {
+ @apply bg-neutral-100 text-neutral-500 [&>div]:bg-neutral-500;
+ @variant dark {
+ @apply bg-neutral-950 text-neutral-300 [&>div]:bg-neutral-300;
+ }
+ }
+}
diff --git a/styles/components/button.css b/styles/components/button.css
new file mode 100644
index 0000000..2337a24
--- /dev/null
+++ b/styles/components/button.css
@@ -0,0 +1,63 @@
+@layer components {
+ .rustlanges-button {
+ @apply flex h-12 w-fit cursor-pointer items-center justify-center gap-2.5 px-8 transition [&>svg]:size-6;
+
+ @variant disabled {
+ @apply cursor-not-allowed;
+ }
+ }
+
+ .rustlanges-button--primary {
+ @apply rounded-2xl border;
+ @apply bg-primary-500 shadow-rb-black border-black text-black;
+ @apply hover:bg-primary-600 active:shadow-none;
+
+ @variant disabled {
+ @apply border-neutral-400 bg-neutral-100 text-neutral-400 shadow-none;
+ }
+
+ @variant dark {
+ @apply bg-primary-300 hover:bg-primary-400 disabled:bg-neutral-950;
+ }
+ }
+
+ .rustlanges-button--secondary {
+ @apply rounded-2xl border;
+ @apply bg-light shadow-rb-neutral-950 border-neutral-950 text-neutral-950;
+ @apply active:shadow-none;
+
+ @variant disabled {
+ @apply border-neutral-400! bg-neutral-100 text-neutral-400 shadow-none;
+ }
+
+ @variant hover {
+ @apply shadow-rb-primary-500 border-primary-500 text-primary-500;
+ }
+
+ @variant dark {
+ @apply bg-dark text-light border-light shadow-rb-neutral-50;
+ @apply disabled:bg-neutral-950;
+ }
+ }
+
+ .rustlanges-button--text {
+ @apply hover:text-primary-600 text-neutral-950;
+
+ @variant dark {
+ @apply text-light hover:text-primary-300;
+ }
+ }
+
+ .rustlanges-button--icon {
+ @apply p-2! aspect-square !h-fit rounded-full border;
+ @apply bg-light border-black text-black;
+
+ @variant hover {
+ @apply text-primary-500 border-primary-500;
+ }
+
+ @variant dark {
+ @apply bg-dark border-light text-light;
+ }
+ }
+}
diff --git a/styles/components/chip.css b/styles/components/chip.css
new file mode 100644
index 0000000..6c53cb3
--- /dev/null
+++ b/styles/components/chip.css
@@ -0,0 +1,46 @@
+@layer components {
+ .rustlanges-chip {
+ @apply flex w-fit cursor-default items-center justify-center gap-1 transition;
+ @apply [&>svg]:size-3.5;
+ }
+
+ .rustlanges-chip--featured {
+ @apply rounded-[20px] border;
+ @apply bg-primary-400 border-black text-black;
+ @apply min-h-[32px];
+ @apply text-sm;
+ @apply p-[4px] px-[12px];
+ }
+
+ .rustlanges-chip--numeric {
+ @apply rounded-[20px] border;
+ @apply bg-primary-200 border-black text-black;
+ @apply min-h-[32px];
+ @apply text-sm;
+ @apply p-[4px] px-[12px];
+ }
+
+ .rustlanges-chip--description {
+ @apply rounded-[20px] border;
+ @apply bg-secondary-200 border-black text-black;
+ @apply min-h-[32px];
+ @apply text-sm;
+ @apply p-[4px] px-[12px];
+ }
+
+ .rustlanges-chip--location {
+ @apply rounded-[20px] border;
+ @apply bg-secondary-200 border-black text-black;
+ @apply min-h-[24px];
+ @apply text-sm;
+ @apply p-[2px] px-[12px];
+ }
+
+ .rustlanges-chip--small {
+ @apply rounded-[20px] border;
+ @apply bg-secondary-200 border-black text-black;
+ @apply min-h-[22px];
+ @apply text-xs;
+ @apply p-[2px] px-[8px];
+ }
+}
diff --git a/styles/components/collaborators.css b/styles/components/collaborators.css
new file mode 100644
index 0000000..9fa9284
--- /dev/null
+++ b/styles/components/collaborators.css
@@ -0,0 +1,9 @@
+@layer components {
+ .rustlanges-collaborators {
+ @apply flex h-12 w-full items-center justify-between;
+ }
+
+ .rustlanges-collaborators__avatars {
+ @apply flex w-fit items-center;
+ }
+}
diff --git a/styles/components/dropdown.css b/styles/components/dropdown.css
new file mode 100644
index 0000000..7c88597
--- /dev/null
+++ b/styles/components/dropdown.css
@@ -0,0 +1,45 @@
+@layer components {
+ .rustlanges-dropdown {
+ @apply relative grid w-fit;
+ }
+
+ .rustlanges-dropdown__view {
+ @apply shadow-rb-black flex h-6 items-center gap-1 overflow-hidden rounded-sm border border-black;
+ @apply text-[12px];
+ }
+
+ .rustlanges-dropdown__view-dot {
+ @apply ml-2 size-1 rounded-full;
+ }
+
+ .rustlanges-dropdown__view-icon {
+ @apply size-6 rounded-r-sm border-l border-l-black;
+ @apply bg-white text-black;
+
+ @variant dark {
+ @apply bg-dark text-neutral-50;
+ }
+ }
+
+ .rustlanges-dropdown__content {
+ @apply absolute left-0 top-full mt-2 w-full transition duration-200;
+ }
+
+ .rustlanges-dropdown__content--open {
+ @apply visible opacity-100;
+ }
+
+ .rustlanges-dropdown__content--closed {
+ @apply invisible opacity-0;
+ }
+
+ .rustlanges-dropdown__list {
+ @apply shadow-rb-black grid gap-1.5 px-2 py-1.5 transition;
+ @apply rounded-sm border border-black;
+ @apply dark:bg-dark bg-white;
+ }
+
+ .rustlanges-dropdown__list-item {
+ @apply cursor-pointer;
+ }
+}
diff --git a/styles/components/flap.css b/styles/components/flap.css
new file mode 100644
index 0000000..f8b8647
--- /dev/null
+++ b/styles/components/flap.css
@@ -0,0 +1,40 @@
+@layer components {
+ .rustlanges-flap {
+ @apply relative flex justify-center gap-2;
+ @apply h-6 w-20 px-5;
+
+ @variant desktop {
+ @apply h-[47.5px] w-[167.5px] px-8;
+ }
+ }
+
+ .rustlanges-flap__svg {
+ @apply desktop:px-3 absolute left-0 top-0 z-0 h-full w-full px-1;
+ }
+
+ .rustlanges-flap--highlight {
+ @apply text-primary-400;
+ }
+
+ .rustlanges-flap--numeric {
+ @apply text-primary-200;
+ }
+
+ .rustlanges-flap--descriptive {
+ @apply text-secondary-400;
+ }
+
+ .rustlanges-flap__view {
+ @apply z-10 flex h-fit w-full items-center justify-center gap-2 text-center font-medium text-neutral-950;
+ @apply desktop:pt-1;
+ @apply [&>svg]:h-3 [&>svg]:w-3;
+ }
+
+ .rustlanges-flap__view--icon {
+ @apply desktop:*:even:block pt-[3px] *:even:hidden;
+ }
+
+ .rustlanges-flap__view-text {
+ @apply desktop:pt-0 line-clamp-1 pt-px;
+ }
+}
diff --git a/styles/components/level.css b/styles/components/level.css
new file mode 100644
index 0000000..96e82d9
--- /dev/null
+++ b/styles/components/level.css
@@ -0,0 +1,22 @@
+@layer components {
+ .rustlanges-level {
+ @apply rounded-xl border border-black px-2 leading-[150%];
+ @apply desktop:text-sm text-xxs;
+ }
+
+ .rustlanges-level--n1 {
+ @apply bg-primary-100;
+ }
+
+ .rustlanges-level--n2 {
+ @apply bg-primary-300;
+ }
+
+ .rustlanges-level--n3 {
+ @apply bg-primary-500;
+ }
+
+ .rustlanges-level--op {
+ @apply bg-secondary-400;
+ }
+}
diff --git a/styles/components/radio.css b/styles/components/radio.css
new file mode 100644
index 0000000..0d965ad
--- /dev/null
+++ b/styles/components/radio.css
@@ -0,0 +1,13 @@
+@layer components {
+ .rustlanges-radio {
+ @apply shadow-rb-black aspect-square appearance-none transition;
+ @apply flex size-4 items-center justify-center rounded-full border border-black;
+ @apply dark:bg-dark bg-white;
+ @apply checked:after:bg-primary-500;
+
+ @variant after {
+ @apply absolute size-2 rounded-full transition;
+ @apply bg-gray dark:bg-neutral-500;
+ }
+ }
+}
diff --git a/styles/components/tag.css b/styles/components/tag.css
new file mode 100644
index 0000000..740d08f
--- /dev/null
+++ b/styles/components/tag.css
@@ -0,0 +1,20 @@
+@layer components {
+ .rustlanges-tag {
+ @apply grid h-7 cursor-pointer place-items-center rounded-[20px] border px-2 text-xs font-semibold transition;
+ }
+
+ .rustlanges-tag--default {
+ @apply bg-light border-black text-black;
+
+ @variant dark {
+ @apply border-neutral-50 bg-neutral-950 text-neutral-50;
+ }
+ }
+ .rustlanges-tag--selected {
+ @apply bg-secondary-100 border-secondary-600 text-secondary-600;
+
+ @variant dark {
+ @apply bg-primary-950 border-primary-500 text-primary-500;
+ }
+ }
+}
diff --git a/styles/components/text.css b/styles/components/text.css
new file mode 100644
index 0000000..178afdc
--- /dev/null
+++ b/styles/components/text.css
@@ -0,0 +1,119 @@
+@layer components {
+ .text-h1 {
+ font-size: 32px;
+ font-weight: 700;
+ line-height: 125%;
+ letter-spacing: 0.32%;
+
+ @apply desktop:text-[48px];
+ }
+
+ .text-h2 {
+ font-size: 32px;
+ font-weight: 700;
+ line-height: 140%;
+ letter-spacing: 0.24%;
+
+ @apply desktop:text-[32px];
+ }
+
+ .text-h3 {
+ font-size: 24px;
+ font-weight: 600;
+ line-height: 150%;
+ letter-spacing: 0.2%;
+
+ @apply desktop:text-[28px];
+ }
+
+ .text-h4 {
+ font-size: 18px;
+ font-weight: 600;
+ line-height: 160%;
+ letter-spacing: 0.18%;
+
+ @apply desktop:text-[24px];
+ }
+
+ .text-h5 {
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 160%;
+ letter-spacing: 0.08%;
+
+ @apply desktop:text-[20px];
+ }
+
+ .text-h6 {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 160%;
+ letter-spacing: 0.07%;
+
+ @apply desktop:text-[18px];
+ }
+
+ .text-subtitle-1 {
+ font-size: 16px;
+ line-height: 175%;
+ letter-spacing: 0.08%;
+
+ @apply desktop:text-[20px];
+ }
+
+ .text-subtitle-2 {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 160%;
+ letter-spacing: 0.08%;
+
+ @apply desktop:text-[16px];
+ }
+
+ .text-paragraph-1 {
+ font-size: 14px;
+ line-height: 160%;
+ letter-spacing: 0;
+
+ @apply desktop:text-[16px];
+ }
+
+ .text-paragraph-2 {
+ font-size: 12px;
+ line-height: 150%;
+ letter-spacing: 0;
+
+ @apply desktop:text-[14px];
+ }
+
+ .text-button {
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 160%;
+ letter-spacing: 0.28%;
+
+ @apply desktop:text-[16px];
+ }
+
+ .text-caption {
+ font-size: 12px;
+ line-height: 150%;
+ letter-spacing: 0.06%;
+ }
+
+ .text-overline {
+ font-size: 10px;
+ font-weight: 600;
+ line-height: 150%;
+ letter-spacing: 1%;
+ }
+
+ .heading {
+ font-size: 28px;
+ font-weight: 700;
+ line-height: 125%;
+ letter-spacing: 0.32%;
+
+ @apply desktop:text-[40px];
+ }
+}
diff --git a/styles/package.json b/styles/package.json
new file mode 100644
index 0000000..82f7787
--- /dev/null
+++ b/styles/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "@rustlanges/styles",
+ "private": false,
+ "version": "0.0.1",
+ "type": "module",
+ "exports": {
+ ".": "./tailwindcss.css",
+ "./bundled.css": "./dist/bundled.css",
+ "./breakpoints": "./breakpoints.css",
+ "./components": "./components.css",
+ "./tailwindcss": "./tailwindcss.css",
+ "./theme": "./theme.css",
+ "./utilities": "./utilities.css",
+ "./components/*": "./components/*.css",
+ "./utilities/*": "./utilities/*.css"
+ },
+ "files": [
+ "dist",
+ "components",
+ "utilities",
+ "breakpoints.css",
+ "components.css",
+ "package.json",
+ "safelist.txt",
+ "tailwindcss.css",
+ "theme.css",
+ "utilities.css"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/RustLangES/design-system-components",
+ "directory": "styles"
+ },
+ "scripts": {
+ "build": "npx @tailwindcss/cli -i tailwindcss.css -o dist/bundled.css --minify"
+ },
+ "peerDependencies": {},
+ "devDependencies": {
+ "@tailwindcss/cli": "^4.1.8",
+ "tailwindcss": "^4.1.8"
+ },
+ "dependencies": {}
+}
diff --git a/styles/safelist.txt b/styles/safelist.txt
new file mode 100644
index 0000000..23144b2
--- /dev/null
+++ b/styles/safelist.txt
@@ -0,0 +1 @@
+shadow-rb-black
diff --git a/styles/tailwindcss.css b/styles/tailwindcss.css
new file mode 100644
index 0000000..a27a068
--- /dev/null
+++ b/styles/tailwindcss.css
@@ -0,0 +1,10 @@
+@layer theme, base, components, utilities;
+
+@import "tailwindcss/theme.css" layer(theme);
+@import "tailwindcss/utilities.css" layer(utilities);
+
+@import "./theme.css";
+
+@import "./breakpoints.css";
+@import "./components.css";
+@import "./utilities.css";
diff --git a/styles/theme.css b/styles/theme.css
new file mode 100644
index 0000000..8e96562
--- /dev/null
+++ b/styles/theme.css
@@ -0,0 +1,140 @@
+@theme static {
+ /* ----- Breakpoints ----- */
+ --breakpoint-desktop: 360px;
+
+ /* ------ Colors ------ */
+ --color-primary-50: #fff6ed;
+ --color-primary-100: #ffebd4;
+ --color-primary-200: #ffd2a8;
+ --color-primary-300: #ffb270;
+ --color-primary-400: #ff8637;
+ --color-primary-500: #ff6e1f;
+ --color-primary-600: #f04906;
+ --color-primary-700: #c63407;
+ --color-primary-800: #9e2a0e;
+ --color-primary-900: #7f260f;
+ --color-primary-950: #450f05;
+
+ --color-secondary-50: #f4f3ff;
+ --color-secondary-100: #ebe9fe;
+ --color-secondary-200: #d9d6fe;
+ --color-secondary-300: #bcb5fd;
+ --color-secondary-400: #9a8afb;
+ --color-secondary-500: #7a5bf7;
+ --color-secondary-600: #6637ee;
+ --color-secondary-700: #5927da;
+ --color-secondary-800: #3e1c96;
+ --color-secondary-900: #3e1c96;
+ --color-secondary-950: #240f66;
+
+ --color-neutral-50: #f6f6f6;
+ --color-neutral-100: #e7e7e7;
+ --color-neutral-200: #d1d1d1;
+ --color-neutral-300: #b0b0b0;
+ --color-neutral-400: #888888;
+ --color-neutral-500: #6d6d6d;
+ --color-neutral-600: #5d5d5d;
+ --color-neutral-700: #4f4f4f;
+ --color-neutral-800: #454545;
+ --color-neutral-900: #3d3d3d;
+ --color-neutral-950: #222222;
+
+ --color-error-50: #fff1f1;
+ --color-error-100: #fee5e5;
+ --color-error-200: #fdced2;
+ --color-error-300: #fba6ac;
+ --color-error-400: #f87480;
+ --color-error-500: #f04257;
+ --color-error-600: #dd2140;
+ --color-error-700: #ba1636;
+ --color-error-800: #9c1533;
+ --color-error-900: #851632;
+ --color-error-950: #4a0717;
+
+ --color-warning-50: #fdf7e9;
+ --color-warning-100: #faebc7;
+ --color-warning-200: #f7d591;
+ --color-warning-300: #f0b042;
+ --color-warning-400: #eb9a24;
+ --color-warning-500: #dc8316;
+ --color-warning-600: #bd6211;
+ --color-warning-700: #974411;
+ --color-warning-800: #7d3816;
+ --color-warning-900: #6b2e18;
+ --color-warning-950: #3e160a;
+
+ --color-success-50: #effef5;
+ --color-success-100: #dafee9;
+ --color-success-200: #b8fad4;
+ --color-success-300: #80f5b4;
+ --color-success-400: #48e78f;
+ --color-success-500: #19ce6b;
+ --color-success-600: #0eab55;
+ --color-success-700: #0f8646;
+ --color-success-800: #12693a;
+ --color-success-900: #115632;
+ --color-success-950: #03301a;
+
+ --color-light: #fafafa;
+ --color-dark: #2e2e2e;
+ --color-gray: #d9d9d9;
+
+ /* Fonts */
+ --text-xxs: 10px;
+ --text-caption: 12px;
+ --text-caption--font-weight: 400;
+ --text-caption--letter-spacing: 0em;
+ --text-caption--line-height: 16px;
+ --text-caption-bold: 12px;
+ --text-caption-bold--font-weight: 500;
+ --text-caption-bold--letter-spacing: 0em;
+ --text-caption-bold--line-height: 16px;
+ --text-body: 14px;
+ --text-body--font-weight: 400;
+ --text-body--letter-spacing: 0em;
+ --text-body--line-height: 20px;
+ --text-body-bold: 14px;
+ --text-body-bold--font-weight: 500;
+ --text-body-bold--letter-spacing: 0em;
+ --text-body-bold--line-height: 20px;
+ --text-heading-3: 18px;
+ --text-heading-3--font-weight: 500;
+ --text-heading-3--letter-spacing: 0em;
+ --text-heading-3--line-height: 20px;
+ --text-heading-2: 24px;
+ --text-heading-2--font-weight: 500;
+ --text-heading-2--letter-spacing: 0em;
+ --text-heading-2--line-height: 24px;
+ --text-heading-1: 32px;
+ --text-heading-1--font-weight: 500;
+ --text-heading-1--letter-spacing: 0em;
+ --text-heading-1--line-height: 36px;
+ --text-monospace-body: 14px;
+ --text-monospace-body--font-weight: 400;
+ --text-monospace-body--letter-spacing: 0em;
+ --text-monospace-body--line-height: 20px;
+
+ /* Font families */
+ --font-caption: Inter;
+ --font-caption-bold: Inter;
+ --font-body: Inter;
+ --font-body-bold: Inter;
+ --font-heading-3: Inter;
+ --font-heading-2: Inter;
+ --font-heading-1: Inter;
+ --font-monospace-body: monospace;
+
+ /* Border radiuses */
+ --radius-sm: 8px;
+ --radius-md: 16px;
+ --radius-DEFAULT: 16px;
+ --radius-lg: 24px;
+ --radius-full: 9999px;
+
+ /* Spacing */
+ --spacing-112: 28rem;
+ --spacing-144: 36rem;
+ --spacing-192: 48rem;
+ --spacing-256: 64rem;
+ --spacing-320: 80rem;
+}
diff --git a/styles/utilities.css b/styles/utilities.css
new file mode 100644
index 0000000..e54e1f1
--- /dev/null
+++ b/styles/utilities.css
@@ -0,0 +1,3 @@
+@import "./utilities/container.css";
+@import "./utilities/scollbar.css";
+@import "./utilities/shadow.css";
diff --git a/styles/utilities/container.css b/styles/utilities/container.css
new file mode 100644
index 0000000..e9e2da3
--- /dev/null
+++ b/styles/utilities/container.css
@@ -0,0 +1,29 @@
+@utility container {
+ padding-left: 16px;
+ padding-right: 16px;
+
+ @media (width >= theme(--breakpoint-sm)) {
+ padding-left: calc((100vw + 16px - 640px) / 2);
+ padding-right: calc((100vw + 16px - 640px) / 2);
+ }
+
+ @media (width >= theme(--breakpoint-md)) {
+ padding-left: calc((100vw + 16px - 768px) / 2);
+ padding-right: calc((100vw + 16px - 768px) / 2);
+ }
+
+ @media (width >= theme(--breakpoint-lg)) {
+ padding-left: calc((100vw + 16px - 1024px) / 2);
+ padding-right: calc((100vw + 16px - 1024px) / 2);
+ }
+
+ @media (width >= theme(--breakpoint-xl)) {
+ padding-left: calc((100vw + 16px - 1280px) / 2);
+ padding-right: calc((100vw + 16px - 1280px) / 2);
+ }
+
+ @media (width >= theme(--breakpoint-2xl)) {
+ padding-left: calc((100vw + 16px - 1536px) / 2);
+ padding-right: calc((100vw + 16px - 1536px) / 2);
+ }
+}
diff --git a/styles/utilities/scrollbar.css b/styles/utilities/scrollbar.css
new file mode 100644
index 0000000..75a391e
--- /dev/null
+++ b/styles/utilities/scrollbar.css
@@ -0,0 +1,26 @@
+@layer utilities {
+ @supports not selector(::-webkit-scrollbar) {
+ .scrollbar {
+ scrollbar-color: var(--color-neutral-300) var(--color-neutral-100);
+
+ @variant dark {
+ scrollbar-color: var(--color-neutral-900) var(--color-neutral-600);
+ }
+ }
+ }
+
+ .scrollbar::-webkit-scrollbar {
+ @apply w-4;
+ }
+
+ .scrollbar::-webkit-scrollbar-track {
+ @apply rounded-full bg-neutral-100 dark:bg-neutral-900;
+ }
+
+ .scrollbar::-webkit-scrollbar-thumb {
+ @apply rounded-full bg-neutral-300 transition dark:bg-neutral-600;
+ }
+ .scrollbar::-webkit-scrollbar-thumb:hover {
+ @apply opacity-90;
+ }
+}
diff --git a/styles/utilities/shadow.css b/styles/utilities/shadow.css
new file mode 100644
index 0000000..ef35839
--- /dev/null
+++ b/styles/utilities/shadow.css
@@ -0,0 +1,6 @@
+@utility shadow-rb-* {
+ box-shadow: 1px 1px 0 0 --value(--color- *);
+ @media (min-width: 360px) {
+ box-shadow: 2px 2px 0 0 --value(--color- *);
+ }
+}