Tailwind乃"仅CSS耳。"然则何哉?确然岂非
bg-blue-500 text-white px-6 py-3 rounded-lg font-bold shadow-md决意乎?吾撰五百行之纯范JavaScript转换器,取Tailwind类字符串,印其等价CSS,兼有实时预览。此程实为Tailwind设计之明训,胜于阅其文牍。
寰宇示范:https://sen.ltd/portfolio/tw-to-css/
包裹GitHub:JHSNS_URL_0
为何筑此?
二因:
- 多"Tailwind教程"未显其成之CSS。读者习类名,乃依纹摹,非悟其各一之用。。左右并列之视,使地图可见。
-
。自为之,乃学Tailwind之设计最速之道。。既书
bg-{color}-{shade}为应之器,乃知Tailwind之感何其一贯——同形者,遍于诸用之族。
此器非Tailwind之重施也。乃百用之小宗,盖取教程与速启所常用者。志在明其对应,非欲运生产之地也.
架构
tailwind-data.js ← Lookup tables: colour palette, spacing scale, font sizes, …
parser.js ← Tokenizer + handler array (no DOM, Node-testable)
app.js ← UI glue: input → parser → live preview + CSS output
parser.js导出parse(input),其返{ rules, unrecognised }。全流为纯——node --test不启浏览器,行四十三案而未尝辍。
處理器之列 — 加一工具 = 加一功能
每個 Tailwind 之用是其一條。HANDLERS数组也。解析器依序试之;首非空者胜。
const HANDLERS = [
// exact-match utilities
layoutHandler("flex", [["display", "flex"]]),
layoutHandler("flex-col", [["flex-direction", "column"]]),
layoutHandler("hidden", [["display", "none"]]),
// prefix utilities driven by a callback
prefixHandler("bg-", (v) => {
const hex = resolveColor(v);
return hex ? [["background-color", hex]] : null;
}),
prefixHandler("text-", (v) => {
if (v in FONT_SIZE) { const [s, l] = FONT_SIZE[v]; return [["font-size", s], ["line-height", l]]; }
if (["left","center","right","justify"].includes(v)) return [["text-align", v]];
const hex = resolveColor(v);
return hex ? [["color", hex]] : null;
}),
// spacing utilities share one helper because they all key into SPACING_MAP
spacingHandler("p", "padding"),
spacingHandler("px", ["padding-left", "padding-right"]),
spacingHandler("py", ["padding-top", "padding-bottom"]),
spacingHandler("w", "width"),
spacingHandler("h", "height"),
// ...
];
此形乃Tailwind之所以通贯一气之由也。器名者,前缀加之,尺度之钥也。,其制一也0, 0.5, 1, 2, 4, 8, …数字遍于四方。施行之,则迫尔内化焉。
类名重载(其)text-问题)
text-此乃 Tailwind 中最繁冗之前缀也:
| 类 | 何所立也 |
|---|---|
text-sm |
字體大小加行高 |
text-center |
左对齐 |
text-blue-500 |
色 |
一愚解器择其一而破其余。其真应之则次第遣之。
prefixHandler("text-", (v) => {
// 1. font-size scale first (sm, md, lg, xl, 2xl, 3xl, ...)
if (v in FONT_SIZE) {
const [size, lh] = FONT_SIZE[v];
return [["font-size", size], ["line-height", lh]];
}
// 2. text alignment keywords
if (["left","center","right","justify"].includes(v)) {
return [["text-align", v]];
}
// 3. fall through to colour resolution
const hex = resolveColor(v);
return hex ? [["color", hex]] : null;
});
次第为要,非率尔而为之也——Tailwind其自身亦能解析之text-{x}依此序,故text-sm恒胜于一假设之色名sm與text-blue-500屬於同色之範疇,蓋因blue-500之形制不協FONT_SIZE之鍵。
此理亦適用於border-(色與寬之別),以及其他若干。
间距之標度,僅僅是n * 0.25rem
const SPACING_BASE = [
0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12,
14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96,
];
for (const n of SPACING_BASE) {
SPACING_MAP[String(n)] = n === 0 ? "0px" : `${n * 0.25}rem`;
}
SPACING_MAP["px"] = "1px";
SPACING_MAP["auto"] = "auto";
SPACING_MAP["full"] = "100%";
此即整個間距之體系。p-4是也padding: 1rem.mt-8是。margin-top: 2rem。gap-2是。gap: 0.5rem君所内化于 Tailwind 之 8px 网格直觉者0.25rem * 2 = 0.5rem ≈ 8px at default 16px root font— 于此矣。
方向快捷键(px,py,mx,my此乃一言以蔽之也。
function spacingHandler(prefix, cssProp) {
return (cls) => {
if (!cls.startsWith(`${prefix}-`)) return null;
const v = cls.slice(prefix.length + 1);
if (!(v in SPACING_MAP)) return null;
const props = Array.isArray(cssProp) ? cssProp : [cssProp];
return props.map((p) => [p, SPACING_MAP[v]]);
};
}
// usage:
spacingHandler("px", ["padding-left", "padding-right"]);
胜者为王
parse("bg-red-500 bg-blue-500")
// → { rules: [
// { class: "bg-red-500", declarations: [["background-color", "#ef4444"]] },
// { class: "bg-blue-500", declarations: [["background-color", "#3b82f6"]] },
// ] }
二则皆得存,然toCSS()坍然聚为一块,待其后胜者。
export function toCSS(rules, selector = ".preview") {
const seen = new Map();
for (const r of rules) {
for (const [prop, value] of r.declarations) {
seen.set(prop, value); // overwrites earlier value for same prop
}
}
// ...
}
此合乎 Tailwind 之直觉,谓"于某属性,所书之末班胜"。于 Tailwind 之实,此以 CSS 源序及 JIT 编译为之——然于静态班列,Map.set越规有序,等也。
(真Tailwind则更为精妙)@layer系统之组件与实用优先,然于单类列表内,其规一也。)
测不可见者
故也parser.js纯粹无杂,每项效用皆可单元测试。
test("text-blue-500", () => {
assert.deepEqual(classToDeclarations("text-blue-500"),
[["color", "#3b82f6"]]);
});
test("h-screen → 100vh", () => {
assert.deepEqual(classToDeclarations("h-screen"),
[["height", "100vh"]]);
});
test("w-screen → 100vw, NOT 100vh", () => {
assert.deepEqual(classToDeclarations("w-screen"),
[["width", "100vw"]]);
});
test("invalid class returns null", () => {
assert.equal(classToDeclarations("not-a-real-class"), null);
});
四十三试,覆词化,每用器族,其text-命发之序,不配之径,及CSS之呈。所谓"横屏与纵屏之试"者,实为真之守卫——早于实施之际,吾曾书之100vh二者皆非,宽之谬也。
未施之事(及其故)
-
变体(
hover:,md:,dark:此需生成媒体查询/伪类包装器。非"名→值"演示范围。 -
任意之值(
p-[17px],bg-[#abcdef]— 易增,然所求者标准之主题,非Tailwind之重施也。 - 插件 / 专属之主题 — 理由同前。
若欲此等,则处理器数组之构架为当之选:每一者皆仅增处理器(或为变体而绕现有者)。
试之
将汝项目之真实Tailwind类粘贴于此,观其CSS。若某物解析为空预览,则属"未识"之列,汝将知其故.
启示
- Tailwind大抵为一五十行之查表也者,默认主题之色彩谱系、明暗层次、间距标度、字体大小、字重也。其余乃命名之范式。
-
bg-{color}-{shade}者,前缀加键,入二维之表。者,施之则设计之域昭然。 -
text-者,意有所指而重载之。者,大小、对齐、色彩三者互竞。序次派遣(先大小,次对齐,终色彩)则歧义自消。 -
胜者为王独列一单,则表折矣
Map.set依序通览解析之规——无需玩弄具体之巧。 - 使解析器无DOM之累,则
node --test遍覆诸用支。
此乃SEN LLC(东京)之开源组合第241号。吾辈持续递送精微利器:https://sen.ltd/portfolio/













