@carlonicora/nextjs-jsonapi (英语)
一个全面的Next.js软件包为JSON:API兼容客户端提供统一的服务器/客户端支持,自动缓存,以及完整的shadcn/ui组件库.
目录
特征
- 统一 API: 自动检测环境(服务器/客户端)并使用适当的请求方法
- JSON:API 合规: 完整的 JSON: API 规格支持与去线化和平面化
- 下一个.js 16+ 缓存: 内建支持
cacheLife()财务报告和审定财务报表cacheTag()通过缓存配置文件 - 反应钩数字 :
useJsonApiGet财务报告和审定财务报表useJsonApiMutation用于获取客户端数据 - 服务器组件: 直接服务器侧取取数据并自动处理符号
- 多用途支助: B2B 应用程序的内建公司ID处理
- 文件上传: 无连接文件上传支持, 有多部分请求
- 阴影/: 44个预建的UI组件(41个标准+3个自定义)
- 工具函数数字 :
cn类合并、移动检测和参考组成
建筑
图书馆按这些入境点排列 exports 地图在 package.json:
@carlonicora/nextjs-jsonapi
├── (main) # Unified API, configuration functions, and everything in /core
├── /core # Interfaces, factories, registries, permissions, feature modules, and utilities
├── /server # Server-side requests, ServerSession, and cache tag helpers
├── /cached-get # cachedServerGet: the token-free "use cache" GET (server only)
├── /client # React hooks and client-side utilities
├── /components # Library components and the 44 shadcn/ui components
├── /contexts # React providers (SessionProvider, ViewportProvider, SocketProvider, ...)
├── /contexts/styles.css # Stylesheet for the contexts entry
├── /testing # Test utilities (mock providers, render helpers)
├── /billing # Stripe billing components (kept apart so Stripe.js is not loaded globally)
├── /help # Help feature, client components
├── /help/server # Help feature, server-safe constants and types
├── /tokenusage # Token-usage admin feature, client components
├── /analytics # Analytics feature, client components
├── /analytics/server # Analytics feature, server page components
└── /next-config # createNextConfig for next.config.js
4: 0的改变
4.0使图书馆在下16.4缓存组件下工作. 完整的食谱,随着破碎的改变和工作顺序,在 文件/迁移至4.0.md简言之:
- 缓存组件数字 :
createNextConfig打开cacheComponents财务报告和审定财务报表partialPrefetching。应用程序框架在它后面的会话流出时在静态外壳中制作. - 会议数字 :
ServerSession.read()(单位:千美元)/server返回 aPromise<SessionSnapshot>由饼干和信头建造。 不用等就叫它来 答应我SessionProvider(单位:千美元)/contexts(三);useSession()解开它,所以它的召唤者坐在它的后面<Suspense>。 。 。 . - 供应商接受
session数字 :ViewportProvider, (中文(简体) ),SocketProvider(单位:千美元)/contexts和SidebarProvider(单位:千美元)/components接受session?: Promise<SessionSnapshot>. 旧道具(token, (中文(简体) ),initialIsMobile, (中文(简体) ),defaultOpen还行. createNextConfig从/next-config数字 :cspRemove不见了 CSP的第三方来自services(单位:千美元)"stripe" | "googleMaps" | "turnstile" | "discord" | "googleFonts"); 任何其它内容csp。 。 。 .- 缓存: 一个模块的
cache值是一个cacheLife配置文件名称或标签。 缓存服务器通过cachedServerGet(单位:千美元)/cached-get页:1"use cache"函数,从不发送符号。 见 快取配置文件。 。 。 . configureI18n需求getLocale:通过下一轮getLocale从next-intl/server; 图书馆不再导入下层 .
安装
pnpm add @carlonicora/nextjs-jsonapi
Git 子模块设置( 替代)
如果您想要将软件包用作git子模块(用于开发或npm发布前):
1. 添加子模块
cd /path/to/your-project
git submodule add https://github.com/carlonicora/nextjs-jsonapi packages/nextjs-jsonapi
2. 核实有效
git submodule status
# Should show: <commit-sha> packages/nextjs-jsonapi (heads/master)
3. 提交子模块
git add .gitmodules packages/nextjs-jsonapi
git commit -m "Add nextjs-jsonapi as submodule"
4. 更新您的 package.json (例如, apps/web/package.json(中文(简体) )
{
"dependencies": {
"@carlonicora/nextjs-jsonapi": "workspace:*"
}
}
5. 确保 pnpm-workspace.yaml 包括软件包
packages:
- "apps/*"
- "packages/*"
6. 安装和建造
pnpm install
cd packages/nextjs-jsonapi && pnpm build && cd ../..
CI/CD(GitHub 动作),添加 submodules: recursive 到您的退出步骤 :
- uses: actions/checkout@v4
with:
submodules: recursive
用子模块克隆一个项目:
# When cloning fresh
git clone --recurse-submodules https://github.com/your/repo.git
# If already cloned
git submodule update --init --recursive
同伴依赖关系
| 软件包 | 版本 | 需求 | 目的 |
|---|---|---|---|
next | >=16.4.0 | 对 | 下一个框架 |
react | >=19.3.0 | 对 | 反应库 |
react-dom | >=19.3.0 | 对 | 反应 DOM |
react-hook-form | 页:1 | 可选 | 表格处理(形式组件) |
快速启动
1. 配置API客户端
在您的环境设置文件中配置 JSON: API 客户端 :
// src/config/env.ts
import { configureJsonApi } from "@carlonicora/nextjs-jsonapi";
import { bootstrap } from "@/config/Bootstrapper";
configureJsonApi({
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
bootstrapper: bootstrap,
});
2. 定义您的模块和起步器
为模块注册(用于 Modules.X 和 DataClass Registry (用于 JSON: API 响应翻译):
// src/config/Bootstrapper.ts
import { DataClassRegistry, FieldSelector, ModuleRegistry } from "@carlonicora/nextjs-jsonapi/core";
import { ModuleWithPermissions } from "@carlonicora/nextjs-jsonapi/core";
import { S3Module } from "@carlonicora/nextjs-jsonapi/core";
// Import your module definitions
import { ArticleModule } from "@/features/article/ArticleModule";
import { UserModule } from "@/features/user/UserModule";
import { Article } from "@/features/article/data/Article";
import { User } from "@/features/user/data/User";
// Module factory helper
const moduleFactory = (params: {
pageUrl?: string;
name: string;
cache?: string;
model: any;
feature?: string;
moduleId?: string;
inclusions?: Record<string, { types?: string[]; fields?: FieldSelector<any>[] }>;
}): ModuleWithPermissions => ({
pageUrl: params.pageUrl,
name: params.name,
model: params.model,
feature: params.feature,
moduleId: params.moduleId,
cache: params.cache,
inclusions: params.inclusions ?? {},
});
// Example module definition file (e.g., ArticleModule.ts)
// export const ArticleModule = (factory: ModuleFactory) =>
// factory({ name: "articles", model: Article, pageUrl: "/articles" });
// Single source of truth for all modules
const allModules = {
Article: ArticleModule(moduleFactory),
User: UserModule(moduleFactory),
S3: S3Module(moduleFactory), // Built-in S3 module from library
} satisfies Record<string, ModuleWithPermissions>;
// Export type for TypeScript autocompletion
export type AllModuleDefinitions = typeof allModules;
let bootstrapped = false;
export function bootstrap(): void {
if (bootstrapped) return;
// Register modules for Modules.X access
Object.entries(allModules).forEach(([name, module]) => {
ModuleRegistry.register(name, module);
});
// Register model classes for JSON:API response translation
DataClassRegistry.bootstrap(allModules);
bootstrapped = true;
}
3. 在服务器组件中获取数据
// src/app/articles/page.tsx
import { JsonApiGet } from "@carlonicora/nextjs-jsonapi";
import { Modules } from "@carlonicora/nextjs-jsonapi/core";
export default async function ArticlesPage() {
const response = await JsonApiGet({
classKey: Modules.Article,
endpoint: "/articles",
language: "en",
});
if (!response.ok) {
return <div>Error: {response.error}</div>;
}
return (
<ul>
{response.data.map((article) => (
<li key={article.id}>{article.title}</li>
))}
</ul>
);
}
4. 在客户端组件中使用钩子
"use client";
import { useJsonApiGet, useJsonApiMutation } from "@carlonicora/nextjs-jsonapi/client";
import { Modules } from "@carlonicora/nextjs-jsonapi/core";
export function ArticleList() {
const { data, loading, error, refetch } = useJsonApiGet({
classKey: Modules.Article,
endpoint: "/articles",
});
const { mutate, loading: creating } = useJsonApiMutation({
method: "POST",
classKey: Modules.Article,
onSuccess: () => refetch(),
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<button
onClick={() => mutate({
endpoint: "/articles",
body: { title: "New Article" }
})}
disabled={creating}
>
Create Article
</button>
<ul>
{data.map((article) => (
<li key={article.id}>{article.title}</li>
))}
</ul>
</div>
);
}
环境变量
# Required
NEXT_PUBLIC_API_URL=http://localhost:3000
# Optional - Token cookie name (default: "token")
# Set this if your API uses a different cookie name for JWT tokens
条目点
主导出( E).(中文(简体) )
默认导出提供了自动检测环境的统一 API :
import {
JsonApiGet,
JsonApiPost,
JsonApiPut,
JsonApiPatch,
JsonApiDelete,
configureJsonApi,
} from "@carlonicora/nextjs-jsonapi";
核心项目/core(中文(简体) )
核心接口、工厂、登记册和公用事业:
import {
// Interfaces
ApiDataInterface,
ApiRequestDataTypeInterface,
ApiResponseInterface,
// Factories
JsonApiDataFactory,
// Registries
ModuleRegistry, // Register modules during bootstrap
DataClassRegistry, // Register model classes for JSON:API translation
Modules, // Access registered modules (e.g., Modules.Article)
// Endpoint builder
EndpointBuilder,
// Field selectors
FieldSelector,
// Utilities
translateResponse,
} from "@carlonicora/nextjs-jsonapi/core";
客户/client(中文(简体) )
反应钩和客户端公用事业(要求) "use client":
import {
// Hooks
useJsonApiGet,
useJsonApiMutation,
useRehydration,
// Context
JsonApiProvider,
useJsonApiContext,
// Request utilities
directFetch,
getClientToken,
} from "@carlonicora/nextjs-jsonapi/client";
服务器( U)/server(中文(简体) )
服务器端请求工具 :
import {
serverRequest,
getServerToken,
ServerSession, // read(), isLogged(), checkPermission(), ...
invalidateCacheTag,
invalidateCacheTags,
updateCacheTag,
CACHE_PROFILES,
} from "@carlonicora/nextjs-jsonapi/server";
缓冲/cached-get(中文(简体) )
免签 "use cache" 获取。 它决定了真正的功能 react-server 仅限条件;其他捆绑都有一个会扔出活塞的活塞. serverRequest 召人来见你们 快取配置文件) (中文(简体) ).
import { cachedServerGet, statusFromDigest } from "@carlonicora/nextjs-jsonapi/cached-get";
权限( 在 /core(中文(简体) )
权限检查工具输出自 /core (并主入道:
import {
checkPermissions,
Action,
type PermissionCheck,
type ModuleWithPermissions,
type ModuleFactory,
} from "@carlonicora/nextjs-jsonapi/core";
特性模块(在 /core(中文(简体) )
可直接用于应用程序的内建功能模块:
import {
// S3 Module (for file uploads via pre-signed URLs)
S3Module, // Module definition factory
S3Service, // Service with getPreSignedUrl, getSignedUrl, deleteFile
S3, // Data class
type S3Interface, // Response interface
type S3Input, // Input parameters
} from "@carlonicora/nextjs-jsonapi/core";
// Usage example:
const s3Response = await S3Service.getPreSignedUrl({
key: "companies/123/documents/file.pdf",
contentType: "application/pdf",
isPublic: true,
});
await fetch(s3Response.url, {
method: "PUT",
headers: s3Response.headers,
body: file,
});
公用事业 /core(中文(简体) )
工具函数 :
import {
cn, // Class name merger (clsx + tailwind-merge)
composeRefs, // Compose multiple refs
useComposedRefs, // Hook for composing refs
useIsMobile, // Mobile detection hook
type ClassValue, // Type for cn function
} from "@carlonicora/nextjs-jsonapi/core";
构成部分(/components(中文(简体) )
库本身的组件加上所有shadcn/ui组件(需要) "use client":
import {
// UI Components (41)
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
Alert,
AlertTitle,
AlertDescription,
AlertDialog,
AlertDialogTrigger,
AlertDialogContent /* ... */,
Avatar,
AvatarImage,
AvatarFallback,
Badge,
badgeVariants,
Breadcrumb,
BreadcrumbList,
BreadcrumbItem /* ... */,
Button,
buttonVariants,
Calendar,
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend /* ... */,
Checkbox,
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
Command,
CommandInput,
CommandList,
CommandItem /* ... */,
ContextMenu,
ContextMenuTrigger,
ContextMenuContent /* ... */,
Dialog,
DialogTrigger,
DialogContent,
DialogHeader /* ... */,
Drawer,
DrawerTrigger,
DrawerContent /* ... */,
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent /* ... */,
Form,
FormField,
FormItem,
FormLabel,
FormControl /* ... */,
HoverCard,
HoverCardTrigger,
HoverCardContent,
Input,
Label,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem /* ... */,
Popover,
PopoverTrigger,
PopoverContent,
Progress,
RadioGroup,
RadioGroupItem,
ScrollArea,
ScrollBar,
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem /* ... */,
Separator,
Sheet,
SheetTrigger,
SheetContent,
SheetHeader /* ... */,
Sidebar,
SidebarProvider,
SidebarContent,
SidebarMenu /* ... */,
Skeleton,
Slider,
Sonner,
Toaster,
toast,
Switch,
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell /* ... */,
Tabs,
TabsList,
TabsTrigger,
TabsContent,
Textarea,
Toggle,
toggleVariants,
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
// Custom Components (3)
Kanban,
KanbanColumn,
KanbanItem, // Drag-and-drop Kanban board
Link, // next-intl compatible Link
MultiSelect, // Multi-select dropdown
} from "@carlonicora/nextjs-jsonapi/components";
其他条目
| 入境点 | 它所持有的 |
|---|---|
/contexts | 反应提供者 : SessionProvider编号 :useSession, (中文(简体) ), ViewportProvider, (中文(简体) ), SocketProvider和更多 |
/contexts/styles.css | 随上下文条目一起运出样式表 |
/testing | MockJsonApiProvider, (中文(简体) ), renderWithProviders, (中文(简体) ), createMockApiData, (中文(简体) ), createMockResponse,, , , , , , , , . |
/billing | 条纹计费组件和上下文 |
/help | 帮助功能客户端组件( U)HelpProvider, (中文(简体) ), HelpHeader, (...) (中文(简体) ) |
/help/server | 服务器安全 HELP_MODES 和帮助文章类型 |
/tokenusage | Token-usage 附加功能客户端组件 |
/analytics | 分析功能客户端组件( A)AnalyticsTracker, (...) (中文(简体) ) |
/analytics/server | AnalyticsAdminPage, (中文(简体) ), AnalyticsJourneyPage 服务器组件 |
/next-config | createNextConfig 页:1 next.config.js (见 文件/迁移至4.0.md(中文(简体) ) |
/messages/* | en.json, (中文(简体) ), it.json: 图书馆提供的文本(见 文献/图书馆(中文(简体) ) |
图书馆自带英文和意大利文;一个应用程序根据自己的信息将其与 mergeMessages 说明 文献/图书馆。 。 。 .
统一 API
统一的API自动检测代码是否运行在服务器或客户端上,并使用适当的请求方法.
JsonApiGet (英语)
从 JSON: API 端点获取数据 :
const response = await JsonApiGet({
classKey: Modules.Article, // Module definition
endpoint: "/articles/123", // API endpoint
companyId: "company-uuid", // Optional: for multi-tenant apps
language: "en", // Required: for i18n
});
if (response.ok) {
console.info(response.data); // Deserialized data
console.info(response.pagination); // Pagination info
// Navigate pages
if (response.next) {
const nextPage = await response.nextPage();
}
}
JsonApi 邮局
创建新资源 :
const response = await JsonApiPost({
classKey: Modules.Article,
endpoint: "/articles",
body: { title: "New Article", content: "..." },
language: "en",
// Optional
files: { attachment: file }, // File uploads
overridesJsonApiCreation: false, // Use raw body instead of JSON:API format
responseType: Modules.OtherType, // If response type differs
});
JsonApiPut / JsonApiPatch 密码
更新资源 :
const response = await JsonApiPut({
classKey: Modules.Article,
endpoint: "/articles/123",
body: { title: "Updated Title" },
language: "en",
});
JsonApiDelete (英语)
删除资源 :
const response = await JsonApiDelete({
classKey: Modules.Article,
endpoint: "/articles/123",
language: "en",
});
客户钩
使用 JsonApiGet 软件
自动调试获取数据的钩 :
const {
data, // Fetched data or null
loading, // Loading state
error, // Error message or null
response, // Full API response
refetch, // Manual refetch function
hasNextPage, // Pagination: has next page
hasPreviousPage, // Pagination: has previous page
fetchNextPage, // Fetch next page
fetchPreviousPage, // Fetch previous page
} = useJsonApiGet<Article>({
classKey: Modules.Article,
endpoint: `/articles/${id}`,
companyId: companyId,
options: {
enabled: !!id, // Conditionally enable
deps: [someDependency], // Refetch when these change
},
});
使用 JsonApi 组合
钩子变异(POST, PUT, PATCH, DELETE):
const {
data, // Result data or null
loading, // Loading state
error, // Error message or null
response, // Full API response
mutate, // Execute the mutation
reset, // Reset state
} = useJsonApiMutation<Article>({
method: "POST",
classKey: Modules.Article,
onSuccess: (data) => console.info("Created:", data),
onError: (error) => console.error("Failed:", error),
});
// Execute mutation
const result = await mutate({
endpoint: "/articles",
body: { title: "New Article" },
files: { image: imageFile },
companyId: "company-uuid",
});
服务器请求
对于服务器组件或API路由,直接使用服务器模块:
import { serverRequest, getServerToken } from "@carlonicora/nextjs-jsonapi/server";
// Per-user data: no `cache`, so this is a plain uncached fetch that sends the token
export async function getMyArticle(id: string) {
return serverRequest({
method: "GET",
url: `${process.env.NEXT_PUBLIC_API_URL}/articles/${id}`,
token: await getServerToken(),
language: "en",
});
}
// Shared data: a GET with `cache` goes through cachedServerGet and never sends the token
export async function getPublicArticle(id: string) {
return serverRequest({
method: "GET",
url: `${process.env.NEXT_PUBLIC_API_URL}/articles/${id}`,
cache: "articles", // a cacheLife profile name, or a tag
language: "en",
});
}
快取配置文件
一个模块 cache 值决定服务器端 GET 如何被缓存 。 每一个得到一个 cache 值在内部运行 cachedServerGet页:1 "use cache" 函数 :
- a 内置
cacheLife配置文件名称( e)"seconds", (中文(简体) ),"minutes", (中文(简体) ),"hours", (中文(简体) ),"days", (中文(简体) ),"weeks", (中文(简体) ),"max", (中文(简体) ),"default"页:1cacheLife(profile)· ; - 任何其它值都是标记 ~
cacheTag(value)+ 键cacheLife("max"),所以该条目将一直存在,直到您取消标记.
// In your module definition
const ArticleModule = (factory: ModuleFactory) =>
factory({ name: "articles", model: Article, cache: "articles" }); // tag
// JsonApiGet on the server applies it automatically, and does not read the token
// cookie for a cached module
// After a write, expire the tag
await updateCacheTag("articles"); // Server Actions: the same request reads fresh data
await invalidateCacheTag("articles"); // elsewhere: stale-while-revalidate
缓存路径是无符号的: 参数是缓存密钥, 因此请求会不包含 Authorization。需要一个符号的终点是每个用户,其模块不能设置 cache。失败的响应被扔出而不是存储;其状态在 error.digest 作为 HTTP_<status> 财务报告和审定财务报表 statusFromDigest 读回.
权限
检查保护资源的用户权限 :
import { Action, checkPermissions, Modules } from "@carlonicora/nextjs-jsonapi/core";
// Check if user has permission
const canEdit = checkPermissions({
module: Modules.Article,
action: Action.Update,
user: currentUser,
data: article,
});
if (!canEdit) {
return <div>Access denied</div>;
}
阴影/
该包包括44个预建的shadcn/ui组件:
标准UI组件(41)
| 构成部分 | 说明 |
|---|---|
Accordion | 可折叠的内容部分 |
Alert | 对重要消息的点名 |
AlertDialog | 确认的模块对话框 |
Avatar | 用户配置图像 |
Badge | 状况指标和标签 |
Breadcrumb | 导航面包屑 |
Button | 单击带有变体的动作 |
Calendar | 摘取日期日历 |
Card | 内容容器 |
Carousel | 滑动内容面板 |
Chart | 数据可视化(Recharts) |
Checkbox | 切换选项 |
Collapsible | 可扩展区域 |
Command | 命令调色板( cmdk) |
ContextMenu | 右键菜单 |
Dialog | 模块窗口 |
Drawer | 滑动侧面板(Vaul) |
DropdownMenu | 下放菜单 |
Form | 表格处理(反应- hook-form) |
HoverCard | 交接卡 |
Input | 文本输入字段 |
Label | 表格标签 |
NavigationMenu | 导航菜单 |
Popover | 浮动内容 |
Progress | 进度指标 |
RadioGroup | 无线电按钮组 |
ScrollArea | 自定义滚动条 |
Select | 下拉选择 |
Separator | 视觉分隔符 |
Sheet | 侧面小组讨论 |
Sidebar | 应用程序边栏 |
Skeleton | 正在装入占位符 |
Slider | 区域滑动器 |
Sonner | 托拉斯通知 |
Switch | 切换开关 |
Table | 数据表 |
Tabs | 标签界面 |
Textarea | 多行文本输入 |
Toggle | 切换按钮 |
Tooltip | 隐藏工具提示 |
自定义组件(3)
| 构成部分 | 说明 |
|---|---|
Kanban | 拖放 Kanban 棋盘( dnd- kit) |
Link | 下个兼容链接包 |
MultiSelect | 多选择带徽章的下拉 |
用法示例
"use client";
import {
Button,
Card,
CardHeader,
CardTitle,
CardContent,
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
} from "@carlonicora/nextjs-jsonapi/components";
import { cn } from "@carlonicora/nextjs-jsonapi/core";
export function ArticleCard({ article, className }) {
return (
<Card className={cn("hover:shadow-lg transition-shadow", className)}>
<CardHeader>
<CardTitle>{article.title}</CardTitle>
</CardHeader>
<CardContent>
<p>{article.excerpt}</p>
<Dialog>
<DialogTrigger>
<Button variant="outline">Read More</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{article.title}</DialogTitle>
</DialogHeader>
<p>{article.content}</p>
</DialogContent>
</Dialog>
</CardContent>
</Card>
);
}
尾风 CSS 配置
对 Tailwind v4 很重要: 您必须添加 @source 给您的指令 globals.css 以确保 Tailwind 扫描软件包组件文件 :
/* apps/web/src/app/globals.css */
@import "tailwindcss";
@import "tw-animate-css";
/* Include package source files for Tailwind to scan */
@source "../../../../packages/nextjs-jsonapi/src/**/*.{ts,tsx}";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
/* ... other theme variables */
}
这个 @source 路径应该是相对的 globals.css 切换到软件包 src 目录.
CSS 变量
Shadcn/ui 组件需要您在应用程序中定义 CSS 变量 。 把这些添加到您的 globals.css数字 :
:root {
/* Background & Foreground */
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
/* Primary */
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
/* Secondary */
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
/* Muted */
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
/* Accent */
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
/* Destructive */
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
/* Border & Input */
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
/* Card & Popover */
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
/* Charts */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
/* Sidebar */
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
/* Warning */
--warning: oklch(0.84 0.16 84);
--warning-foreground: oklch(0.28 0.07 46);
/* Radius */
--radius: 0.625rem;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.269 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
/* ... other dark mode values */
}
许可证
此项目在GPL v3下获得开源使用许可.
商业/封闭源码许可证,联系: @卡尔隆科拉
作者
卡罗・尼科拉 @卡尔隆科拉