UNPKG

mancha

Version:

Javscript HTML rendering engine

488 lines (450 loc) 16.3 kB
import * as fs from "node:fs"; import * as path from "node:path"; import rules, { COLOR_OPACITY_MODIFIERS, DURATIONS, MEDIA_BREAKPOINTS, PERCENTS, PROPS_COLORS, PROPS_CUSTOM, PROPS_SIZING, PROPS_SIZING_MINMAX, PROPS_SPACING, } from "../src/css_gen_utils.js"; const DOCS_PATH = path.join(process.cwd(), "docs", "05_css.md"); function handledBySize(klass: string) { return klass.startsWith("text-") && (klass.endsWith("px") || klass.endsWith("rem")); } function generateMarkdown() { let md = "# CSS Documentation\n\n"; md += "Mancha provides a set of CSS utilities and minimal styles to help you build your application.\n\n"; md += "## Minimal CSS\n\n"; md += 'The minimal CSS rules provide a clean, readable default style for standard HTML elements. You can inject them using `injectCss(["minimal"])` or by adding `css="minimal"` to your script tag.\n\n'; md += "### Reset & Defaults\n"; md += "- **Max Width**: 70ch (centered)\n"; md += "- **Padding**: 2em 1em\n"; md += "- **Line Height**: 1.75\n"; md += "- **Font Family**: sans-serif\n"; md += "- **H1-H6 Margin**: 1em 0 0.5em\n"; md += "- **P, UL, OL Margin Bottom**: 1em\n\n"; md += "## Basic CSS\n\n"; md += 'The basic CSS rules provide a more comprehensive reset and set of defaults, widely based on Tailwind CSS Preflight. You can inject them using `injectCss(["basic"])` or by adding `css="basic"` to your script tag.\n\n'; md += "### Key Features\n"; md += "- **Box Sizing**: `border-box` globally\n"; md += "- **Typography**: Default sans-serif font stack, consistent line-height\n"; md += "- **Form Elements**: Inherit font styles, transparent backgrounds, `cursor: pointer` for buttons\n"; md += "- **Media**: Images/videos max-width 100%\n"; md += "- **Dialog**: Default backdrop styling\n"; md += "- **Resets**: Removes default margins/paddings from most block elements\n\n"; md += "## Utility CSS\n\n"; md += 'The utility CSS rules are inspired by Tailwind CSS. You can inject them using `injectCss(["utils"])` or by adding `css="utils"` to your script tag.\n\n'; md += "### Media Breakpoints\n\n"; md += "| Prefix | Min Width |\n"; md += "| --- | --- |\n"; for (const [bp, width] of Object.entries(MEDIA_BREAKPOINTS)) { md += `| \`${bp}:\` | \`${width}px\` |\n`; } md += "\n"; md += "### Pseudo States\n\n"; md += "The following pseudo states are supported for all utilities:\n"; md += "- `hover:`\n"; md += "- `focus:`\n"; md += "- `disabled:`\n\n"; md += "### Spacing (Margin & Padding)\n\n"; md += "Spacing utilities use a 0.25rem (4px) unit by default.\n\n"; md += "| Prefix | Property | Values |\n"; md += "| --- | --- | --- |\n"; for (const [prop, prefix] of Object.entries(PROPS_SPACING)) { md += `| \`${prefix}-\` | \`${prop}\` | \`0\`, \`0.25rem\` - \`128rem\` (and negative versions) |\n`; md += `| \`${prefix}x-\` | \`${prop}-left/right\` | Same as above |\n`; md += `| \`${prefix}y-\` | \`${prop}-top/bottom\` | Same as above |\n`; md += `| \`${prefix}t-\` | \`${prop}-top\` | Same as above |\n`; md += `| \`${prefix}b-\` | \`${prop}-bottom\` | Same as above |\n`; md += `| \`${prefix}l-\` | \`${prop}-left\` | Same as above |\n`; md += `| \`${prefix}r-\` | \`${prop}-right\` | Same as above |\n`; } md += "\n"; md += "### Sizing (Width & Height)\n\n"; md += "| Prefix | Property | Values |\n"; md += "| --- | --- | --- |\n"; for (const [prop, prefix] of Object.entries(PROPS_SIZING)) { md += `| \`${prefix}-\` | \`${prop}\` | \`0\`, \`full\` (100%), \`screen\` (100vw/vh), \`auto\`, \`px\`, \`0.25rem\` - \`128rem\` |\n`; md += `| \`${prefix}-sm/md/lg/xl\` | \`${prop}\` | Match media breakpoints (e.g. \`w-sm\`, \`max-w-lg\`) |\n`; md += `| \`${prefix}-dvw/dvh/svw/svh/lvw/lvh\` | \`${prop}\` | Viewport-relative units |\n`; md += `| \`${prefix}-fit/min/max\` | \`${prop}\` | \`fit-content\`, \`min-content\`, \`max-content\` |\n`; } for (const [prop, prefix] of Object.entries(PROPS_SIZING_MINMAX)) { md += `| \`${prefix}-\` | \`${prop}\` | Same as sizing |\n`; } md += "| `size-` | `width` & `height` | `auto`, `px`, `full`, `dvw`, `dvh`, `svw`, `svh`, `lvw`, `lvh`, `min`, `max`, `fit` |\n"; md += "\n"; md += "### Colors\n\n"; md += "Supported prefixes: `text-`, `bg-`, `border-`, `fill-`.\n\n"; md += "| Color | Shades |\n"; md += "| --- | --- |\n"; md += "| `white`, `black`, `transparent` | N/A |\n"; for (const [color, _shades] of Object.entries(PROPS_COLORS)) { md += `| \`${color}\` | \`50\`, \`100\`, \`200\`, \`300\`, \`400\`, \`500\`, \`600\`, \`700\`, \`800\`, \`900\` |\n`; } md += "\n"; md += `You can also control the opacity of any color utility by appending \`/{opacity}\` (${COLOR_OPACITY_MODIFIERS.join(", ")}):\n`; md += "- `bg-black/50`\n"; md += "- `text-red-500/20`\n"; md += "- `border-blue-600/100`\n"; md += "\n"; md += "### Borders & Corners\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const borderProps = ["border", "rounded"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (borderProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document dynamic border widths md += "| `border-{0-15}` | Border width in pixels (e.g., `border-0`, `border-1`, ..., `border-15`) |\n"; md += "| `border-x-{0-15}` | Horizontal border width in pixels |\n"; md += "| `border-y-{0-15}` | Vertical border width in pixels |\n"; md += "| `border-{t,b,l,r}-{0-15}` | Individual side border width in pixels |\n"; md += "\n"; md += "### Shadows & Effects\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const effectProps = ["shadow", "ring", "mix-blend-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (effectProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document opacity md += "| `opacity-0` | Fully transparent |\n"; md += `| \`opacity-{${PERCENTS.join(",")}}\` | Opacity values (multiples of 5) |\n`; md += "\n"; md += "### Outline\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const outlineProps = ["outline"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (outlineProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Aspect Ratio\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const aspectProps = ["aspect-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (aspectProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Backdrop Filters\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const backdropProps = ["backdrop-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (backdropProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Transitions & Animations\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const animProps = ["transition", "animate-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (animProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document durations md += `| \`duration-{${DURATIONS.join(",")}}\` | Transition duration in milliseconds |\n`; md += "\n"; md += "### Interactivity\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const interProps = ["cursor-", "select-", "pointer-events-", "resize", "user-select"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (interProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Typography\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const typoProps = [ "font-", "text-", "italic", "not-italic", "leading-", "tracking-", "uppercase", "lowercase", "capitalize", "truncate", "whitespace-", "decoration-", "underline", "line-through", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (typoProps.some((p) => klass.startsWith(p)) && !handledBySize(klass)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Flexbox & Layout\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const flexProps = [ "flex", "justify-", "items-", "self-", "content-", "grow", "shrink", "gap-", "order-", "grid-", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (flexProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document gap utilities md += "| `gap-0` | No gap |\n"; md += "| `gap-{1-512}` | Gap in rem units (0.25rem increments) |\n"; md += "| `gap-{1-512}px` | Gap in pixels |\n"; md += "| `gap-x-{1-512}` | Horizontal gap in rem units |\n"; md += "| `gap-y-{1-512}` | Vertical gap in rem units |\n"; md += "| `gap-x-{1-512}px` | Horizontal gap in pixels |\n"; md += "| `gap-y-{1-512}px` | Vertical gap in pixels |\n"; md += "| `space-x-{0-512}` | Horizontal spacing between children (rem) |\n"; md += "| `space-y-{0-512}` | Vertical spacing between children (rem) |\n"; md += "| `space-x-{0-512}px` | Horizontal spacing between children (px) |\n"; md += "| `space-y-{0-512}px` | Vertical spacing between children (px) |\n"; // Document divide utilities md += "| `divide-x` | Add 1px vertical border between horizontal children |\n"; md += "| `divide-y` | Add 1px horizontal border between vertical children |\n"; md += "| `divide-x-{0,2,4,8}` | Vertical border width between horizontal children |\n"; md += "| `divide-y-{0,2,4,8}` | Horizontal border width between vertical children |\n"; md += "| `divide-solid` | Solid border style for dividers |\n"; md += "| `divide-dashed` | Dashed border style for dividers |\n"; md += "| `divide-dotted` | Dotted border style for dividers |\n"; md += "| `divide-none` | Remove divider borders |\n"; md += "\n"; md += "### Grid Layout\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; // Static grid properties from PROPS_CUSTOM if any (like 'grid') const gridProps = ["grid"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (gridProps.some((p) => klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "| `grid-cols-{1-12}` | `grid-template-columns: repeat(n, minmax(0, 1fr))` |\n"; md += "| `grid-cols-none` | `grid-template-columns: none` |\n"; md += "| `col-span-{1-12}` | `grid-column: span n / span n` |\n"; md += "| `col-span-full` | `grid-column: 1 / -1` |\n"; md += "| `col-start-{1-13}` | `grid-column-start: n` |\n"; md += "| `col-end-{1-13}` | `grid-column-end: n` |\n"; md += "| `col-start/end-auto` | `grid-column-start/end: auto` |\n"; md += "\n"; md += "### Position & Inset\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const posProps = ["relative", "absolute", "fixed", "sticky", "inset-", "object-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (posProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document position values md += "| `top-{0-512}`, `bottom-{0-512}`, `left-{0-512}`, `right-{0-512}` | Position in rem units (0.25rem increments) |\n"; md += "| `top-{0-512}px`, `bottom-{0-512}px`, `left-{0-512}px`, `right-{0-512}px` | Position in pixels |\n"; md += "| `top-{1-100}%`, `bottom-{1-100}%`, `left-{1-100}%`, `right-{1-100}%` | Position in percentages |\n"; md += "| `top-auto`, `bottom-auto`, `left-auto`, `right-auto` | Auto positioning |\n"; // Document z-index md += `| \`z-{${PERCENTS.join(",")}}\` | Z-index values |\n`; md += "\n"; md += "### Display & Visibility\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const displayProps = [ "block", "inline", "hidden", "contents", "visible", "invisible", "collapse", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (displayProps.some((p) => klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Overflow & Scrolling\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const overflowProps = ["overflow-", "overscroll-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (overflowProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Backgrounds\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const bgProps = [ "bg-auto", "bg-cover", "bg-contain", "bg-no-repeat", "bg-fixed", "bg-local", "bg-scroll", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (bgProps.some((p) => klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Lists\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const listProps = ["list-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (listProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Vertical Alignment\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const alignProps = ["align-"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (alignProps.some((p) => klass.startsWith(p))) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Viewport Sizing\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const viewportProps = [ "min-h-screen", "max-h-screen", "min-w-screen", "h-dvh", "h-svh", "h-lvh", "w-dvw", "w-svw", "w-lvw", "min-h-dvh", "min-h-svh", "min-h-lvh", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (viewportProps.some((p) => klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Accessibility\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const a11yProps = ["sr-only", "not-sr-only"]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (a11yProps.some((p) => klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } md += "\n"; md += "### Other Utilities\n\n"; md += "| Utility | Description |\n"; md += "| --- | --- |\n"; const allHandledPrefixes = [ ...typoProps, ...flexProps, ...posProps, ...borderProps, ...effectProps, ...animProps, ...interProps, ...displayProps, ...overflowProps, ...bgProps, ...listProps, ...alignProps, ...a11yProps, ...viewportProps, ...outlineProps, ...aspectProps, ...backdropProps, "w-", "h-", "min-w-", "min-h-", "max-w-", "max-h-", "bg-", "border-", "opacity-", "cursor-", "size-", "top-", "bottom-", "left-", "right-", "z-", "duration-", "gap-", "space-", "divide-", "grid-", "col-", ]; for (const [klass, props] of Object.entries(PROPS_CUSTOM)) { if (!allHandledPrefixes.some((p) => klass.startsWith(p) || klass === p)) { md += `| \`${klass}\` | \`${JSON.stringify(props)}\` |\n`; } } // Document text sizes md += "| `text-{0-99}px` | Font size in pixels (e.g., `text-12px`, `text-16px`) |\n"; md += "| `text-{0-24.75}rem` | Font size in rem units (0.25rem increments) |\n"; md += "\n"; md += "--- \n\n*Generated automatically from `src/css_gen_utils.ts`*\n"; return md; } // Generate CSS and calculate stats const css = rules(); const ruleCount = (css.match(/\{[^}]+\}/g) || []).length; const sizeBytes = css.length; const sizeKB = (sizeBytes / 1024).toFixed(1); const sizeMB = (sizeBytes / 1024 / 1024).toFixed(2); fs.writeFileSync(DOCS_PATH, generateMarkdown()); console.log(`Documentation generated at ${DOCS_PATH}`); console.log(`CSS Stats: ${ruleCount.toLocaleString()} rules, ${sizeKB} KB (${sizeMB} MB)`);