ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

authentik 前端设计系统深度解析:`web/src/styles` 的 CSS 架构、Cascade Layers 与主题体系

authentik 前端设计系统深度解析:`web/src/styles` 的 CSS 架构、Cascade Layers 与主题体系 authentik 前端设计系统深度解析web/src/styles的 CSS 架构、Cascade Layers 与主题体系【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik导读本文基于 authentik 仓库中 web/src/styles/README.md 展开系统梳理这个支撑 Flow、User/Admin 与 Django 静态模板三大前端应用的设计系统Design System。文章将完整覆盖三条顶层 CSS 管线与第四条 shadowDOM 管线、layer层序设计、设计令牌Design Tokens、明暗模式与品牌定制机制并结合仓库内的入口文件、基类组件与主题文件源码进行印证。读完本文你将能快速定位「改一个颜色、调一个间距、定制登录卡片、替换组件样式」在 authentik 前端中各自对应的文件与层也能理解其 CSS 规范设计的来龙去脉。一、设计系统的基石Patternfly 4 与目录使命authentik 前端设计系统起步并至今仍建立在Patternfly 4 CSS Library之上web/src/styles/README.md明确记载了这一依赖关系。web/src/styles目录承载两类职责入口与产物为 authentik 前端的各个应用与工具提供样式入口与构建产物包括 Flowflows.global.css、User/Admininterface.global.css以及为非 SPA 场景的 Django 模板构建的静态 CSSstatic.global.css。组件覆写在 Patternfly 组件之上应用 authentik 自己的设计风格弥补 Patternfly 对 shadowDOM 支持不佳的问题。其中layers.css是每一个入口文件的第一行导入它声明了整套层序layer order及每一层在 CSS 特异性链specificity chain中扮演的角色。该层级设计的目标是让仅凭路径即可读懂一份样式表谁加载它它属于哪个 bundle入口文件的后缀与所处目录在哪里使用位于顶层document还是某个 shadowDOM 组件内部层layer层归属只且只在入口文件中声明角色rolereset、token、layout 或 theme 四类角色之一。二、顶层 CSS 的三条打包管线入口、产物与消费方authentik 的 CSS 通过两种方式打包一是作为顶层 CSS 文档由服务端模板导入到document:root二是被编译为CSSResult 对象由 Web Components 在 shadowDOM 中消费。当前目录下三个以.global.css结尾的文件就是这些顶层 CSS 文档的入口entrypoint它们只负责import与 layer 声明把所有 CSS 资源汇集到文档级样式表中。入口文件由 web/paths/node.js 中的声明匹配第 8694 行分别指向interface.global.css、static.global.css、flows.global.css告知编译器哪些文件应作为构建入口。.global.css这个后缀是必须的——它用于触发 bundler 中正确的引用解析行为。这些入口文件是纯组织性的它们应当只包含指向其他 CSS 文件的import语句。三个入口与消费方的对应关系如下入口source产物output消费方consumersinterface.global.cssdist/styles/interface-*Admin Userflows.global.cssdist/styles/flow-*Flowstatic.global.cssdist/styles/static-*Django 静态模板以 web/src/styles/interface.global.css 为例其结构是典型的入口文件形态先导入layers.css声明层序再按layer(vendor)、layer(theme)、layer(reset)、layer(mode)、layer(components)分组引入对应资源import #styles/layers.css; import #styles/global/vendor/patternfly.css layer(vendor); import goauthentik/fonts/faces.css layer(vendor); import goauthentik/fonts/icons.css layer(vendor); import #styles/global/theme/variables.css layer(theme); import #styles/global/reset/scrollbars.css layer(reset); import #styles/global/reset/globals.css layer(reset); import #styles/global/mode/mode.css layer(mode); import #styles/global/mode/contrast.css layer(mode); import #styles/authentik/components/Placeholder/placeholder.css layer(components); /* …更多 components 层导入… */对比另外两个入口可以看到消费场景的差异web/src/styles/flows.global.css 额外导入了登录相关的authentik/login.css、Login/login-tokens.css、Login/flow-loading.css与 Patternfly 的BackgroundImage以layer(vendor)引入以便覆写而 web/src/styles/static.global.css 则针对不使用 shadowDOM 组件的旧式 Django 模板直接按需导入patternfly/patternfly/components/*的 Button、Form、Login、Title、Avatar 等具体组件样式。三个入口文件的注释都强调了同一条规则层序决定选择器特异性与导入顺序无关同一层内部的冲突仍然遵循「后导入者胜出」last-one-wins。而 CSS 自定义属性Custom Properties允许在引用之后才定义——浏览器在绘制阶段开始时会动态解析并应用特异性最高的那一个版本。三、第四条管线shadowDOM 阴影作用域顶层三条管线之外还有一条贯穿所有 Web Components 的第四条管线shadow scope。authentik 的每个主要 Web Component 都继承自 LitElement 基类 web/src/elements/Base.ts 中的AKElement。该基类会向每一个AKElement的 shadowroot 注入两张样式表shadow/patternfly-base.css$PFBaseshadow/authentik-base.css$AKBase在Base.ts中这两张表通过createStyleSheetUnsafe预编译为CSSStyleSheet实例并在finalizeStyles中按固定顺序组装进每个组件的样式数组const elementStyles [ $PFBase, ...([styles] as Arrayunknown).flat(Infinity), $AKBase, ] as CSSResultOrNative[];注意这里的顺序$PFBase在最前、$AKBase在最后。$PFBase的注释明确要求它必须先于任何依赖 Patternfly 变量的样式被包含提供通用变量与 reset$AKBase则承载对 Patternfly 初始定义的覆写与 authentik 的额外定制。打开 web/src/styles/shadow/patternfly-base.css 可以看到它导入patternfly-common.css、patternfly-globals.css、patternfly-fa-icons.css、patternfly-pf-icons.css并补充了a/button的链接颜色、光标等基础规则而 web/src/styles/shadow/authentik-base.css 是一份组件 CSS 的 barrel 文件一次性把 Alert、Banner、Label、Icon、Page、Button、Dropdown、Drawer、Card、Table、Form、Modal、Wizard、Tooltip 等二十余个组件样式导入每个组件的 shadowroot。为什么这种双层结构可行因为 CSS 自定义属性定义在父元素原生或自定义中时会跨越 shadow 边界影响消费这些属性的子元素的观感。layer指令只控制属性声明在document层的特异性当自定义属性抵达元素边界时当前时刻特异性最高的那个声明胜出。这正是 authentik 让全局设计令牌能穿透 shadowDOM、同时保持各组件样式隔离的根本机制。四、目录蓝图global/与shadow/的分工README 给出了完整的目录结构蓝图结合仓库实际见 web/src/styles 目录可以还原出这套体系的全貌web/src/styles/ layers.css # layer 层序声明 —— 每个入口文件必须最先导入 README.md # 设计系统文档本文主体 interface.global.css # 入口Admin User flows.global.css # 入口Flow static.global.css # 入口Django 模板 global/ # 定义 document 级的设计系统 vendor/ # layer(vendor) vendored PatternFly —— 在 theme 或 components 层覆写 patternfly.css # 从 Patternfly 拉取的 barrel 文件 assets/ # Patternfly CSS 使用的字体 / 图标 webfont reset/ # layer(reset) 顶层归一化 theme/ # layer(theme) authentik 默认设计令牌CSS 自定义属性 mode/ # layer(mode) light/dark/contrast/motion 覆写 locales/ # 按地区ja/ko/zh对表意文字语言的文档级覆写 brand/ # layer(brand) 预留给服务端注入品牌 CSS 自定义属性 shadow/ # 所有 Web Components 的 shadowroot 通用定义 patternfly-base.css # $PFBase authentik-base.css # $AKBase authentik/ # 组件 CSS components/Name/ # 各组件覆写或全新组件 login.css # 登录页布局 令牌桥接 atom/ # CodeMirror 相关 CSS导入到 CodeMirror 组件中需要特别说明的是仓库中global/locales实际包含ja、ko、zh三个子目录每个子目录下有globals.cssREADME 中的规划与现状一致而patternfly/下还有一份constants.tsREADME 中的 TODO 之一见文末。README 也如实指出相当多的组件拥有内嵌 CSS通过 Lit 的css()函数或带有随组件自动打包的伴生文件它们并不完全遵循上述目录方案。为什么部分组件 CSS 被放在这里而非组件目录内仍是一个待厘清的 TODO 项。五、Cascade Layerslayers.css的层序设计层序只在 web/src/styles/layers.css 中声明其内容只有一行核心声明外加详尽的注释layer reset, vendor, components, theme, mode, brand;从最低特异性到最高特异性各层的职责如下层职责约束与备注resetCSS reset顶层归一化目前使用较少——大部分 reset 已并入 PatternflyvendorPatternfly 代码以import … layer(vendor);方式导入冻结禁止手改components文档级组件规则以及把全局令牌桥接到各组件内部自定义属性的root{}块顶层 light-DOM 组件兼容密码管理器等自动化工具每个组件一个:root{}映射条目theme默认设计令牌只放 CSS 自定义属性定义mode为无障碍覆写themelight/dark、高对比、减少动效注意仓库中目前还没有 reduced-motion 文件brand按部署覆写 theme/component 令牌以实现品牌定制数据库品牌覆写仅限自定义属性也可指向自定义 CSS 文件允许高级设计师用::part自由发挥5.1 层作用于选择器而非属性README 特别强调了一个「花了很久才弄明白」的关键点Layers 针对的是选择器selectors不是属性properties。一个同名的 CSS 自定义属性如果被声明在不同选择器中例如:root与.some-inner-value即使位于同一顶层、处于不同层其最终特异性也可能并非你所期望。因此必须保持纪律始终在「字典」容器中声明 CSS 自定义属性——在不同层中都使用:root声明确保它们获得完全相同的特异性。一旦这些属性抵达 shadow 边界当时特异性最高的那个版本胜出。另一个配套结论是导入顺序只在同一层内部有意义。不同层之间后面的层总是让自身的选择器获得比前面层更高的特异性——这保证了brand层可以无条件压过theme层。六、theme 层设计令牌体系theme层只存放 CSS 自定义属性定义是整套设计系统的「词汇表」。在入口文件中由 web/src/styles/global/theme/variables.css 聚合它依次导入colors.css → fonts.css → spacers.css → icons.css → shadows.css → z-indexes.css → borders.cssvariables.css本身还在:root中定义了若干跨层基础令牌例如过渡动画--pf-global--Transition: all 250ms cubic-bezier(0.42, 0, 0.58, 1)、--pf-global--TransitionDuration: 250ms触控目标尺寸--pf-global--target-size--MinWidth: 44px、--pf-global--target-size--MinHeight: 44pxauthentik 品牌强调色--ak-accent: #fd4b2d暗色背景家族--ak-dark-background: #18191a、--ak-dark-background-light: #1c1e21、--ak-dark-background-lighter: #2b2e33侧边栏自动收缩阈值--ak-sidebar--minimum-auto-width: 80rem。它还维护了一套 V2 语义令牌--ak-v2-global--*涵盖背景色、边框、阴影、间距spacer 从xs: 0.25rem到4xl: 5rem、z-index、gutter 等并通过[data-themedark]选择器提供暗色版本再用语义化命名如--ak-v2-global--ContentSurface、--ak-v2-global--PrimaryText把原始令牌映射成含义明确的界面令牌。调色板文件 web/src/styles/global/theme/colors.css 展示了 authentik 的现代化色彩管理方式整套 Patternfly 调色板monochrome、blue、cyan、gold 等统一改用oklab 色彩空间声明如--pf-global--palette--black-100: oklab(0.9851 -0 0)以获得更均匀的感知亮度。字体、间距、阴影、z-index、边框等令牌文件fonts.css、spacers.css、shadows.css、z-indexes.css、borders.css、icons.css与variables.css同处 web/src/styles/global/theme 目录。七、mode 层暗色、高对比与无障碍mode层负责在theme之上提供无障碍相关的覆写light/dark、高对比contrast与减少动效reduced motion。入口文件统一导入 web/src/styles/global/mode/mode.css 与 web/src/styles/global/mode/contrast.css。mode.css揭示了 authentik 暗色模式的实现策略在暗色模式下反转 Patternfly 的明暗变量。它通过html[data-themedark]重写 Patternfly 的整套全局令牌——调色板、背景色、边框色、文字色、链接色、active/primary/info/warning/danger/success 色全部改用 oklab 值例如--pf-global--BackgroundColor--100: oklab(0.2303 -0.0008 -0.0083)。部分颜色特意与 PF4 的默认亮色对齐以保持对比度如--pf-v4-global--palette--blue-300的注释「We diverge from PF4s lighter shade to align closer to the contrast in light mode」。同时它还处理 shadowDOM 内的暗色使用:host([themedark])选择器作用于组件宿主并包含一段「反转的反转」逻辑——因为暗色模式下--pf-global--Color--light-*已被重新赋值为暗色值mode.css末尾的:host([themedark]) .pf-m-dark块负责把这些值恢复为默认注释原文「Our reversal of Patternflys light variables requires us to set the colors back to their defaults when in dark mode」。activeTheme的解析由Base.ts完成connectedCallback中根据ownerDocument.documentElement.dataset.theme或globalAK().brand.uiTheme解析出light | dark并通过theme反射属性同步到组件宿主。README 也坦承存在遗留 TODOmode.css中部分:host规则未被任何东西导入其用途待查。八、brand 层按部署的品牌定制brand层被设计为每个部署per-deployment覆写 theme/component 令牌的保留层为品牌定制提供两条路径数据库品牌覆写仅允许 CSS 自定义属性。Base.ts的构造函数会读取globalAK().brand.brandingCustomCss若有值则通过createStyleSheetUnsafe生成一张自定义样式表并在styleRoot建立时用applyUITheme应用到组件根该属性在源码中被标记为deprecated官方建议改用CSS parts 与自定义属性完成定制注入样式表可能导致跨版本难以维护的脆弱样式。自定义 CSS 文件路径面向高级设计师允许他们使用::part对组件内部元素进行深度定制。在层序上brand位于最末最高特异性因此无论theme层声明了什么默认令牌品牌覆写都能稳定胜出。README 的远期目标中还包括把品牌定制做成产品内的实时预览token 级别以及把主题打包为独立 npm 包交付。九、组件覆写authentik/components/Name/authentik/components/Name/name.css存放在代码中多处出现的 CSS 组件定义——既作为静态页面的顶层定义也被部分 shadowroot 组件使用。它们大多是对 Patternfly 设置的覆写用于确立 authentik 偏好的设计或补偿 Patternfly 对 shadowroot 的不友好。仓库中实际存在的组件目录包括Alert、Banner、Button、Card、Content、Description、Drawer、Dropdown、Fieldset、Form、Icon、Keyboard、Label、Login、Modal、Modifiers、Notification、Page、Placeholder、Screenreader、Scrollbar、Select、Skeleton、Switch、Table、Title、Tooltip、Wizard见 web/src/styles/authentik/components此外authentik/下还有登录布局专用的 web/src/styles/authentik/login.css。组件可以拥有自己的暗色设置通过宿主上的dark以及high-contrast、reduced-motion标记来触发。README 明确了一个演进方向TODO这类组件内暗色规则应尽可能稀少——理想情况下组件应通过 CSS 自定义属性获得全部暗色设置。当前阶段的分流准则是如果样式是组件专属的就放进组件反之才放进mode层。以login.css为例可以看到「组件令牌 → Patternfly 变量」的桥接模式先在:root定义--ak-c-login--MaxWidth: 35rem、--ak-c-login--spacer: clamp(...)等 authentik 专属令牌其中--ak-c-login--PaddingMax: 8dvw等还运用了dvw/dvh视口单位与clamp()实现响应式再在.pf-c-login选择器内把这些令牌映射回--pf-c-login__*变量同时用[data-themedark] .pf-c-login覆写暗色背景。流链接部分则通过::part(list)、::part(list-item)操作 shadowDOM 内的网格布局并针对「恰好 3 个条目」做了两列平衡的特殊处理。十、修改速查想改哪里去哪里找README 最后给出了一份「想改什么 → 去哪个文件」的速查表这是日常前端开发最有价值的索引所有文件均位于web/src/styles下想要修改的内容对应的文件位置登录卡片的宽度 / 内边距authentik/login.css--ak-c-login--MaxWidth等默认颜色 / 间距 / 字体令牌global/theme/colors.css、spacers.css、fonts.css暗色模式的颜色取值global/mode/mode.csslight/dark/contrast/motion 的行为global/mode/mode.css、global/mode/contrast.css表格斑马纹 / 某组件的观感authentik/components/Name/name.css哪些 Patternfly 部件被打进文档global/vendor/patternfly.cssbarrel 文件每个组件 shadow 中获得哪些样式表shadow/patternfly-base.css、shadow/authentik-base.css一个 bundle 包含哪些文件对应的*.global.css入口Cascade-layer 层序layers.css全仓库唯一声明处代码编辑器配色主题atom/one-dark.css十一、演进方向与遗留 TODOREADME 明确声明当前体系是一个中间步骤intermediate step远期目标包括通过独立的包提供 authentik 自己的主题尽可能把组件的硬性定义迁移到与组件同目录的 CSS 文件中把实际用到的 Patternfly CSSvendor 进组件内部提供让品牌定制尽可能简单直接的机制在产品中提供令牌级品牌定制的实时预览。README 同样如实记录了一批遗留 TODO可作为理解代码现状的注脚弄清楚mode.css中:host规则的作用似乎未被任何东西导入决定reset/scrollbars.css的去向它其实更像主题而非 reset但按旧式级联思路被放在了 reset 代码块中为patternfly/constants.ts寻找更合适的归宿厘清authentik/login.css到底在做什么把atom/目录挪到更贴近 CodeMirror 组件的位置。结语web/src/styles是 authentik 前端一切外观的源头三条*.global.css入口决定了 Admin/User、Flow 与 Django 静态模板各自拿到什么 CSSlayers.css用reset → vendor → components → theme → mode → brand的层序构建了可预期的特异性秩序global/theme用 oklab 调色板与语义令牌定义了设计语言shadow/两张基表让每个 Web Component 在 shadowDOM 中共享同一套变量与组件风格而brand层则为多租户品牌定制保留了干净的扩展点。理解了这条「入口 → 层 → 令牌 → 组件 → 阴影」的链路你就掌握了定制 authentik 界面外观的正确姿势改令牌去theme与mode改组件去authentik/components/Name/改打包范围去*.global.css入口改层序去唯一的layers.css。【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表