├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── .npmrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples └── vite-dollars │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── src │ └── main.ts │ └── vite.config.ts ├── flake.lock ├── flake.nix ├── package.json ├── packages ├── brand │ ├── logo-black.svg │ ├── logo-white.svg │ └── logo.svg ├── core │ ├── package.json │ └── src │ │ ├── index.ts │ │ ├── rule-utils │ │ ├── colors.ts │ │ ├── index.ts │ │ ├── logicalSet.ts │ │ ├── sizes.ts │ │ └── time.ts │ │ ├── theme │ │ ├── Theme │ │ │ ├── ThemeColors.ts │ │ │ └── index.ts │ │ └── index.ts │ │ ├── themes │ │ ├── index.ts │ │ ├── material3.ts │ │ ├── tailwind.ts │ │ └── windblade.ts │ │ └── utils │ │ ├── color.ts │ │ └── index.ts ├── unocss-docs │ ├── .gitignore │ ├── build.config.ts │ ├── package.json │ └── src │ │ ├── index.ts │ │ └── types.ts ├── unocss-preset-color │ ├── package.json │ └── src │ │ ├── docs │ │ ├── index.ts │ │ ├── rules.ts │ │ └── usage │ │ │ ├── index.ts │ │ │ ├── installation.ts │ │ │ ├── semanticColors.ts │ │ │ └── theme.ts │ │ ├── index.ts │ │ ├── rules │ │ ├── documented │ │ │ ├── accessibility.ts │ │ │ ├── backgrounds.ts │ │ │ ├── borders.ts │ │ │ ├── interactivity.ts │ │ │ ├── svg.ts │ │ │ └── typography.ts │ │ └── index.ts │ │ └── theme.ts ├── unocss-preset-dollars │ ├── package.json │ └── src │ │ ├── docs │ │ ├── dollarSyntax.ts │ │ ├── index.ts │ │ ├── installation.ts │ │ └── options.ts │ │ ├── index.ts │ │ └── variants │ │ ├── dollars.ts │ │ └── index.ts ├── unocss-preset │ ├── .gitignore │ ├── README.md │ ├── build.config.ts │ ├── package.json │ └── src │ │ ├── docs │ │ ├── index.ts │ │ ├── rules │ │ │ └── index.ts │ │ ├── theme │ │ │ ├── index.ts │ │ │ ├── other.ts │ │ │ └── proportions.ts │ │ └── usage │ │ │ ├── index.ts │ │ │ ├── installation.ts │ │ │ ├── logicalProperties.ts │ │ │ ├── options.ts │ │ │ └── variants.ts │ │ ├── index.ts │ │ ├── preflights │ │ └── index.ts │ │ └── rules │ │ ├── documented │ │ ├── backgrounds.ts │ │ ├── borders.ts │ │ ├── doc-components │ │ │ ├── index.ts │ │ │ ├── iterMultiple.ts │ │ │ └── logical.ts │ │ ├── effects.ts │ │ ├── filters.ts │ │ ├── flexboxAndGrid.ts │ │ ├── interactivity.ts │ │ ├── layout.ts │ │ ├── sizing.ts │ │ ├── spacing.ts │ │ ├── svg.ts │ │ ├── tables.ts │ │ ├── transforms.ts │ │ ├── transitionsAndAnimation.ts │ │ └── typography.ts │ │ └── index.ts └── website │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── postcss.config.cjs │ ├── src │ ├── App.tsx │ ├── api │ │ ├── core.ts │ │ ├── index.ts │ │ ├── modules.tsx │ │ └── modules │ │ │ ├── data.ts │ │ │ └── types.ts │ ├── components │ │ ├── DocsNav.tsx │ │ ├── ModuleSelector.tsx │ │ └── MoludeList.tsx │ ├── index.tsx │ ├── lib │ │ ├── Code.tsx │ │ ├── Container.tsx │ │ ├── Error.tsx │ │ ├── ForMap.tsx │ │ ├── ModuleCard.tsx │ │ ├── ModuleList.tsx │ │ ├── Select.tsx │ │ ├── ShadowDom.tsx │ │ ├── external.ts │ │ ├── rotuer │ │ │ ├── LocalLink.tsx │ │ │ ├── Outlet.tsx │ │ │ ├── Page.tsx │ │ │ ├── index.ts │ │ │ └── integration.ts │ │ ├── uid.ts │ │ └── uno-xml │ │ │ ├── Page.tsx │ │ │ ├── Page │ │ │ ├── Example.tsx │ │ │ ├── For.tsx │ │ │ ├── Sample.tsx │ │ │ ├── TryIt.tsx │ │ │ ├── TryIt │ │ │ │ ├── Utils.tsx │ │ │ │ └── Utils │ │ │ │ │ ├── Input.tsx │ │ │ │ │ ├── Select.tsx │ │ │ │ │ └── Util.tsx │ │ │ └── Viewport.tsx │ │ │ ├── XmlChildren.tsx │ │ │ ├── XmlRoot.tsx │ │ │ ├── XmlVariables.ts │ │ │ └── types.ts │ ├── router │ │ ├── index.tsx │ │ └── pages │ │ │ ├── docs │ │ │ ├── [id].tsx │ │ │ ├── [id] │ │ │ │ ├── [...all].tsx │ │ │ │ └── [nav].tsx │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ └── index │ │ │ ├── calculations │ │ │ ├── tw.html.txt │ │ │ └── wb.html.txt │ │ │ ├── colors │ │ │ ├── tw.html.txt │ │ │ ├── tw.js.txt │ │ │ ├── wb.html.txt │ │ │ └── wb.js.txt │ │ │ ├── fgColors │ │ │ ├── tw.html.txt │ │ │ └── wb.html.txt │ │ │ ├── js │ │ │ ├── tw.js.txt │ │ │ └── wb.js.txt │ │ │ ├── template │ │ │ ├── tw.html.txt │ │ │ ├── tw.js.txt │ │ │ ├── wb.html.txt │ │ │ └── wb.js.txt │ │ │ └── theme │ │ │ ├── tw.js.txt │ │ │ └── wb.js.txt │ ├── stores │ │ ├── docsStore.ts │ │ └── themeStore.ts │ ├── style.css │ └── unocss.ts │ ├── tsconfig.json │ └── vite.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.json └── unocss-preset-windblade ├── README.md ├── package.json └── src └── index.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | indent_size = 2 11 | indent_style = space 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.global.js 2 | build 3 | node_modules 4 | interactive/guides/vendor/*.md 5 | interactive/data/guides.ts 6 | defaultConfig.ts 7 | packages/preset-icons/src/collections.json 8 | packages/eslint-plugin/fixtures 9 | packages/**/submodules 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@antfu"], 3 | "rules": { 4 | "no-restricted-imports": [ 5 | "error", 6 | { 7 | "paths": ["unocss"] 8 | } 9 | ], 10 | "yml/no-empty-document": "off", 11 | "react/no-unknown-property": "off" 12 | }, 13 | "overrides": [ 14 | { 15 | "files": [ 16 | "playground/**/*.*", 17 | "examples/**/*.*", 18 | "test/fixtures/**/*.*" 19 | ], 20 | "rules": { 21 | "no-restricted-imports": "off" 22 | } 23 | }, 24 | { 25 | "files": [ 26 | "**/*.md/*.*" 27 | ], 28 | "rules": { 29 | "no-restricted-imports": "off", 30 | "no-restricted-syntax": "off", 31 | "no-labels": "off", 32 | "@typescript-eslint/no-unused-vars": "off", 33 | "@typescript-eslint/no-var-requires": "off" 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [StarLederer] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .DS_Store 3 | .idea 4 | .nuxt 5 | .output 6 | .temp 7 | *.local 8 | *.log 9 | *.vsix 10 | bench/source/gen*.js 11 | coverage 12 | dist 13 | examples/**/pnpm-lock.yaml 14 | node_modules 15 | packages/runtime/*.global.js 16 | packages/unocss/README.md 17 | packages/vscode/LICENSE 18 | packages/preset-icons/src/collections.json 19 | result.json 20 | interactive/guides/vendor/*.md 21 | interactive/data/guides.ts 22 | interactive/public/play 23 | interactive/public/shiki 24 | .svelte-kit 25 | .eslintcache 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "packages/website/submodules/ui"] 2 | path = packages/website/submodules/ui 3 | url = https://github.com/StarLederer/solid-components.git 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-workspace-root-check=true 2 | # shamefully-hoist=true 3 | strict-peer-dependencies=false 4 | auto-install-peers=true 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "prettier.enable": false, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": true 6 | }, 7 | "editor.formatOnSave": false, 8 | "cSpell.words": [ 9 | "tagify" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ### Added 11 | 12 | - `unocss-preset-dollars` now accepts an option that configures where the dollars come from. 13 | 14 | ### Fixed 15 | 16 | - Incorrect package names in the docs. 17 | 18 | ### Changed 19 | 20 | - . 21 | 22 | ### Removed 23 | 24 | - . 25 | 26 | ## 2.0.0-beta.4 27 | 28 | ### Fixed 29 | 30 | - `grid-fill-...` using `auto-fit` instead of `auto-fill`. 31 | - `grid-fit-...` and `grid-fill` overflowing containers at large sizes. 32 | - Brakets in some grid rules now work much better. 33 | - `place-self-...` and `place-items-...` no longer have a conflicting name. 34 | 35 | ### Changed 36 | 37 | - `$` features are now implemented as a variant and work with utilities from other presets. 38 | - Colors and the `$` variant can now be used as separate presets. 39 | - Documentation is now completely in XML. 40 | - `scheme-auto-` now follows system scheme and is not affected by anthing else. 41 | 42 | ### Removed 43 | 44 | - `scheme-(light|dark)` due to unintiutive specificity. Use `scheme-(light|dark)-`. 45 | 46 | ## 2.0.0-beta.3 47 | 48 | ### Fixed 49 | 50 | - `hidden` now correctly sets `display` to `none`. 51 | - `invisible` now correctly sets `visibility` to `hidden`. 52 | 53 | ### Changed 54 | 55 | - Strictened default font properties. 56 | 57 | ## 2.0.0-beta - 2023-02-17 58 | 59 | ### Added 60 | 61 | - LCH functions to `core/color`. 62 | - `getColorSchemeCSSProps` to `core/color`. 63 | - Brackets to size rules (e.g. `max-size-i-[48ch]`, `max-size-[$lpx]`). 64 | - Multiple selectable default themes (e.g. `presetWindbldade({ theme: 'tailwind' })`). 65 | 66 | ### Fixed 67 | 68 | - `getLCA` & `getLCHA` (ex. `getSLA` & `getHSLA`) now return correct lightness values in light mode. 69 | 70 | ### Changed 71 | 72 | - Semantic colors are now defined with OkLCH. 73 | - Merged `core/variant` into `core/color`. 74 | - `getSLA` & `getHSLA` were replaced with `getLCA` & `getLCHA`. 75 | - `getThemeCSS` now behaves different and is renamed to `getColorSchemeCSSProps`. 76 | - `scheme-...` rule now also accepts `auto` in additon to `dark` and `light` and expects `...-` at the end to set hue. 77 | - `scheme-(light|dark)` now does nothing by itself but overrides `scheme-auto-` behaviour instead. 78 | - Expressions now have to be prefixed with `$` (e.g. `leading-$(1+2)`). 79 | - Theme tokens in expressions and now have to be prefixed with `$` (e.g. `text-($s+$s.2)`). 80 | - Windblade now uses it's own proportions by default. 81 | - Tailwind proprtions do not have `t` appended to them anymore. 82 | 83 | ### Removed 84 | 85 | - HSL functions from `core/color`. 86 | - `getCSSProperties` from `core/color`. 87 | - Highlight rules and interactive colors. 88 | 89 | ## 2.0.0-alpha - 2023-02-06 90 | 91 | ### Added 92 | 93 | - NPM package metadata. 94 | - Bundle scripts using unbuild. 95 | - Dynamic size calculations using mathematical expressions inside `()` (e.g `m-(-s)`, `inset-(m/2)`). 96 | - `getCSSProperties` to `core/color` (ex. `external`). 97 | - `core/variant`. 98 | - `scheme-dark` and `scheme-light` rules. 99 | - `scheme-initial` class (not a rule; does not work in @apply). 100 | - `objToCSS` to `core`. 101 | - Text transform rules. 102 | - New `def` color variations. 103 | - Logical `rounded` (ex. `round`) rule variuants. 104 | - Time units to theme. 105 | - All fitting Tailwind v3.2.4 rules. 106 | 107 | ### Fixed 108 | 109 | - Overwriting light variant color alpha inside the theme not working. 110 | 111 | ### Changed 112 | 113 | - Renamed preset to Windblade. 114 | - Moved source code to `src` directory. 115 | - Renamed `external` to `core`. 116 | - `getSLA` and `getHSLA` in `core` (ex. `external`) now return values for all variants. 117 | - Renamed all size units. 118 | - Size units are now sorted between `units` and `misc` in theme. 119 | - Renamed and improved colors. 120 | - Renamed `theme.sizes.tokens` to `theme.proportions`. 121 | - Renamed `theme.sizes.misc` to `theme.miscSizes`. 122 | - Changed `theme.proportions` (ex. `theme.sizes.tokens`) to match Tailwind. 123 | - Renamed `round` to `rounded`. 124 | - Static and interactive colors are now merged and have optional `interactive` property. 125 | - Renamed `mg` and `pd` to `m` and `p`. 126 | - Renamed `grid-auto-fit` and `grid-auto-fill` to `grid-fit-cols` and `grid-fill-cols`. 127 | - Renamed `min` size to `px`. 128 | - Default transition now matches tailwind. 129 | - Renamed `*-fg-0` to `*-fg-1`, `*-fg-1` to `*-fg-2` etc. 130 | 131 | ### Removed 132 | 133 | - `getColor` from `core` (ex. `external`). 134 | - `width` and `height` rules in favor of `size`. 135 | 136 | ## 1.1.0 - 2023-01-06 137 | 138 | ### Added 139 | 140 | - full (100%), half (50%) and auto size units. 141 | - fill and fill-fg color rules for SVGs. 142 | 143 | ## 1.0.0 - 2023-01-06 144 | 145 | ### Added 146 | 147 | - The first version of this UnoCSS preset. 148 | - This changelog. 149 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-PRESENT Star Lederer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Windblade 4 | 5 | 6 | Windblade 7 |

8 | 9 |

10 | Tailwind-inspired UnoCSS preset with a better color system, logical properties, and simpler customization. 11 |

12 | 13 |

14 | Homepage | Documentation (WIP) | UnoCSS | Tailwind 15 |

16 | 17 | ## Installation 18 | 19 | Please see the [Docs](https://starlederer.github.io/windblade?navigation=/docs/Usage/Installation) for installation instrucions. 20 | 21 | ## Usage 22 | 23 | Documentation is in development. Until it is complete please follow [Tailwind's docs](https://tailwindcss.com/docs/aspect-ratio) and refer to our [documentation](https://starlederer.github.io/windblade?navigation=/docs) for differences. *You will not have to refer to Tailwind's docs once our docuentation is complete.* 24 | 25 | ## Progress 26 | 27 | - [x] All suitable Tailwind utilities implemented (all except animations). 28 | - [x] All rtl-tb utilities replaced with logical counterparts (or removed if not possible). 29 | - [x] Pseudo-classes and pseudo-elements (chose to implement separately via [uoncss-preset-mini-variants](https://github.com/StarLederer/unocss-preset-mini-variants)) 30 | - [x] Automatic color system. 31 | - [x] Javascript access to the automatic color system. 32 | - [x] Detailed README. 33 | - [x] Basic documentation with demos. 34 | - [x] Published to NPM. 35 | - [x] Flexible XML documentation format. 36 | - [ ] Document and reuse features from [@unocss-preset-wind](https://unocss.dev/presets/wind). 37 | - [ ] Better bracket (`...-[...]`) implementation. 38 | - [ ] Documentation search. 39 | - [ ] Listed on [UnoCSS](https://github.com/unocss/unocss) readme. 40 | 41 | ## Sponsors 42 | 43 |

44 | 45 | 46 | 47 |

48 | -------------------------------------------------------------------------------- /examples/vite-dollars/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/vite-dollars/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + @windblade/unocss-preset-dollars 7 | 8 | 9 | 10 |

11 | @windblade/unocss-preset-dollars 12 |

13 | 14 |

15 | Mutates your utils replacing things like $value with anything you define in the config. You can replace dollar names with custom values or even values from the theme! 16 |

17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/vite-dollars/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-dollars", 3 | "type": "module", 4 | "version": "0.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@unocss/core": "0.55.0", 13 | "@windblade/unocss-preset-dollars": "link:../../packages/unocss-preset-dollars", 14 | "typescript": "^5.1.6", 15 | "vite": "^4.4.8" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/vite-dollars/src/main.ts: -------------------------------------------------------------------------------- 1 | import 'uno.css' 2 | -------------------------------------------------------------------------------- /examples/vite-dollars/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import unocss from '@unocss/vite' 3 | import type { Dollar } from '@windblade/unocss-preset-dollars' 4 | import presetDollars from '@windblade/unocss-preset-dollars' 5 | 6 | /** Our custom dollars (key value paris) */ 7 | const dollars: Dollar[] = [ 8 | ['small', 0.5], 9 | ['normal', 1], 10 | ['large', 2], 11 | ] 12 | 13 | export default defineConfig({ 14 | plugins: [ 15 | unocss({ 16 | rules: [ 17 | /** Custom rule that sets `font-size` to whatever is specified after `font-size-` with 'rem' at the end. */ 18 | [/^font-size-(.+)$/, ([_, value]) => { 19 | return { 20 | 'font-size': `${value}rem`, 21 | } 22 | }], 23 | ], 24 | presets: [ 25 | presetDollars({ 26 | // By default the preset will try to use values inside `theme.windlade.propotions` but we don't have that in this setup so we define a custom getter instead. 27 | getVariables: () => dollars, 28 | 29 | // We could also use values from the theme here. 30 | // getVariables: (theme) => Object.entries(theme.something.something), 31 | }), 32 | ], 33 | }), 34 | ], 35 | }) 36 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1676283394, 6 | "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1685566663, 21 | "narHash": "sha256-btHN1czJ6rzteeCuE/PNrdssqYD2nIA4w48miQAFloM=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "4ecab3273592f27479a583fb6d975d4aba3486fe", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "NixOS", 29 | "ref": "23.05", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "flake-utils": "flake-utils", 37 | "nixpkgs": "nixpkgs" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | flake-utils.url = "github:numtide/flake-utils"; 4 | nixpkgs.url = "github:NixOS/nixpkgs/23.05"; 5 | }; 6 | 7 | outputs = { self, flake-utils, nixpkgs }: 8 | flake-utils.lib.eachDefaultSystem (system: 9 | let 10 | pkgs = import nixpkgs { inherit system; }; 11 | pnpm = pkgs.nodePackages.pnpm.override { 12 | nodejs = pkgs.nodejs-18_x; 13 | }; 14 | in { 15 | devShell = pkgs.mkShell rec { 16 | nativeBuildInputs = with pkgs; [ 17 | pnpm 18 | ]; 19 | }; 20 | } 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "windblade", 3 | "type": "module", 4 | "version": "2.0.0-beta.7", 5 | "private": true, 6 | "packageManager": "pnpm@8.6.11", 7 | "scripts": { 8 | "taze": "taze minor -wIr && pnpm -r --parallel run update-post", 9 | "build": "rimraf packages/*/dist && pnpm -r --filter=./packages/* run build && pnpm -r run build-post", 10 | "dev": "pnpm stub", 11 | "lint": "eslint --cache .", 12 | "lint:fix": "nr lint --fix", 13 | "release": "bumpp -r", 14 | "stub": "pnpm -r --filter=./packages/* --parallel run stub", 15 | "typecheck": "tsc --noEmit && pnpm -r --parallel run typecheck" 16 | }, 17 | "devDependencies": { 18 | "@antfu/eslint-config": "^0.40.0", 19 | "@iconify-json/mdi": "^1.1.53", 20 | "@iconify-json/simple-icons": "^1.1.63", 21 | "@types/node": "^20.4.6", 22 | "@unocss/preset-icons": "^0.54.1", 23 | "@unocss/transformer-directives": "^0.54.1", 24 | "@unocss/vite": "0.54.1", 25 | "bumpp": "^9.1.1", 26 | "eslint": "^8.46.0", 27 | "rimraf": "^5.0.1", 28 | "simple-git-hooks": "^2.9.0", 29 | "taze": "^0.11.2", 30 | "typescript": "^5.1.6", 31 | "unbuild": "^1.2.1", 32 | "unocss": "~0.55.0", 33 | "vite": "^4.4.8", 34 | "vite-plugin-favicons-inject": "^2.2.0", 35 | "vite-plugin-solid": "^2.7.0" 36 | }, 37 | "pnpm": { 38 | "overrides": { 39 | "magic-string": "^0.30.0" 40 | } 41 | }, 42 | "simple-git-hooks": { 43 | "pre-commit": "npx lint-staged" 44 | }, 45 | "lint-staged": { 46 | "*.{js,ts,tsx,vue,md}": [ 47 | "eslint --cache --fix" 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/brand/logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/brand/logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/brand/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/core", 3 | "version": "2.0.0-beta.7", 4 | "description": "", 5 | "exports": { 6 | ".": { 7 | "types": "./dist/index.d.ts", 8 | "require": "./dist/index.cjs", 9 | "import": "./dist/index.mjs" 10 | }, 11 | "./*": "./*" 12 | }, 13 | "main": "dist/index.cjs", 14 | "module": "dist/index.mjs", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist", 18 | "*.css" 19 | ], 20 | "scripts": { 21 | "build": "unbuild", 22 | "stub": "unbuild --stub" 23 | }, 24 | "dependencies": { 25 | "@unocss/core": "0.55.0", 26 | "@unocss/preset-mini": "0.55.0", 27 | "better-color-tools": "^0.12.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme' 2 | export { default as theme } from './theme' 3 | export * as utils from './utils' 4 | export * as ruleUtils from './rule-utils' 5 | export * from './themes' 6 | -------------------------------------------------------------------------------- /packages/core/src/rule-utils/colors.ts: -------------------------------------------------------------------------------- 1 | import type { DynamicRule } from '@unocss/core' 2 | import type Theme from '../theme/Theme' 3 | 4 | function colorRule(prefix: string, property: string): DynamicRule { 5 | return [ 6 | new RegExp(`^(${prefix})-(.+)$`), 7 | (match, { theme }) => { 8 | if (theme.windblade?.miscColors?.[match[2]]) { 9 | return { 10 | [property]: match[2], 11 | } 12 | } 13 | 14 | if (theme.windblade?.colors[match[2]]) { 15 | return { 16 | [property]: `var(--${match[2]}-base)`, 17 | } 18 | } 19 | }, 20 | ] 21 | } 22 | 23 | /** 24 | * UnoCSS rule getter specifically for the background property 25 | * 26 | * @param prefix The Atomic CSS class name prefix 27 | * @returns UnoCSS dynamic rule that sets the background to the color defined in the class name as well as sets --fg variables and changes text to var(--fg0) 28 | */ 29 | function colorBgRule(prefix: string): DynamicRule { 30 | return [ 31 | new RegExp(`^(${prefix})-(.+)$`), 32 | (match, { theme }) => { 33 | const color = theme.windblade?.colors[match[2]] 34 | if (!color) 35 | return 36 | 37 | const css: any = { 38 | background: `var(--${match[2]}-base)`, 39 | color: `var(--${match[2]}-fg-1)`, 40 | } 41 | 42 | for (let i = 1; i <= color.on.length; ++i) 43 | css[`--fg-${i}`] = `var(--${match[2]}-fg-${i})` 44 | 45 | return css 46 | }, 47 | ] 48 | } 49 | 50 | function fgColorRule(prefix: string, property: string): DynamicRule { 51 | return [ 52 | new RegExp(`^(${prefix})-(.+)$`), 53 | (match, { theme }) => { 54 | if (theme.windblade?.miscColors?.[match[2]]) { 55 | return { 56 | [property]: match[2], 57 | } 58 | } 59 | 60 | return { 61 | [property]: `var(--fg-${match[2]})`, 62 | } 63 | }, 64 | ] 65 | } 66 | 67 | export { colorRule, colorBgRule, fgColorRule } 68 | -------------------------------------------------------------------------------- /packages/core/src/rule-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * as color from './colors' 2 | export * as logical from './logicalSet' 3 | export * as size from './sizes' 4 | export * as time from './time' 5 | -------------------------------------------------------------------------------- /packages/core/src/rule-utils/logicalSet.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type Theme from '../theme/Theme' 3 | 4 | export const abbreviations = { 5 | axis: { 6 | b: 'block', 7 | i: 'inline', 8 | }, 9 | edges: { 10 | bs: 'block-start', 11 | be: 'block-end', 12 | is: 'inline-start', 13 | ie: 'inline-end', 14 | }, 15 | blockEdges: { 16 | bs: 'block-start', 17 | be: 'block-end', 18 | }, 19 | inlineEdges: { 20 | is: 'inline-start', 21 | ie: 'inline-end', 22 | }, 23 | coners: { 24 | ss: 'start-start', 25 | se: 'start-end', 26 | es: 'end-start', 27 | ee: 'end-end', 28 | }, 29 | } as const 30 | 31 | export type Axis = typeof abbreviations.axis[keyof typeof abbreviations.axis] 32 | export type Edge = typeof abbreviations.edges[keyof typeof abbreviations.edges] 33 | export type BlockEdge = typeof abbreviations.blockEdges[keyof typeof abbreviations.blockEdges] 34 | export type InlineEdge = typeof abbreviations.inlineEdges[keyof typeof abbreviations.inlineEdges] 35 | export type Corner = typeof abbreviations.coners[keyof typeof abbreviations.coners] 36 | 37 | const join = (arr: Array) => (arr.filter(Boolean).join('-')) 38 | 39 | export function axisRules(prefix: string, 40 | postfix: string | undefined, 41 | propertyPrefix: string, 42 | propertyPostfix: string | undefined, 43 | rule: (prefix: string, property: string) => Rule, 44 | ) { 45 | return [ 46 | rule(join([prefix, postfix]), join([propertyPrefix, propertyPostfix])), 47 | rule(join([prefix, 'b', postfix]), join([propertyPrefix, 'block', propertyPostfix])), 48 | rule(join([prefix, 'i', postfix]), join([propertyPrefix, 'inline', propertyPostfix])), 49 | ] 50 | } 51 | 52 | export function edgeRules(prefix: string, 53 | postfix: string | undefined, 54 | propertyPrefix: string, 55 | propertyPostfix: string | undefined, 56 | rule: (prefix: string, property: string) => Rule, 57 | ) { 58 | return [ 59 | rule(join([prefix, postfix]), join([propertyPrefix, propertyPostfix])), 60 | rule(join([prefix, 'b', postfix]), join([propertyPrefix, 'block', propertyPostfix])), 61 | rule(join([prefix, 'bs', postfix]), join([propertyPrefix, 'block-start', propertyPostfix])), 62 | rule(join([prefix, 'be', postfix]), join([propertyPrefix, 'block-end', propertyPostfix])), 63 | rule(join([prefix, 'i', postfix]), join([propertyPrefix, 'inline', propertyPostfix])), 64 | rule(join([prefix, 'is', postfix]), join([propertyPrefix, 'inline-start', propertyPostfix])), 65 | rule(join([prefix, 'ie', postfix]), join([propertyPrefix, 'inline-end', propertyPostfix])), 66 | ] 67 | } 68 | 69 | export function cornerRules(prefix: string, 70 | postfix: string | undefined, 71 | propertyPrefix: string, 72 | propertyPostfix: string | undefined, 73 | rule: (prefix: string, property: string) => Rule, 74 | ) { 75 | return [ 76 | rule(join([prefix, postfix]), join([propertyPrefix, propertyPostfix])), 77 | rule(join([prefix, 'ss', postfix]), join([propertyPrefix, 'start-start', propertyPostfix])), 78 | rule(join([prefix, 'se', postfix]), join([propertyPrefix, 'start-end', propertyPostfix])), 79 | rule(join([prefix, 'ee', postfix]), join([propertyPrefix, 'end-end', propertyPostfix])), 80 | rule(join([prefix, 'es', postfix]), join([propertyPrefix, 'end-start', propertyPostfix])), 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /packages/core/src/rule-utils/sizes.ts: -------------------------------------------------------------------------------- 1 | import type { DynamicRule } from '@unocss/core' 2 | import { handler as h } from '@unocss/preset-mini/utils' 3 | import type { Theme } from '../theme' 4 | import * as logical from './logicalSet' 5 | 6 | export function resolve(value: string, theme: Theme, unit: string) { 7 | // Try to resolve proportion 8 | const token = theme.windblade?.proportions[value] 9 | if (token !== undefined) 10 | return `${token}${unit}` 11 | 12 | // Try to resolve miscSize 13 | const misc = theme.windblade?.miscSizes?.[value] 14 | if (misc !== undefined) 15 | return `${misc}` 16 | 17 | // Try resolving value as a number 18 | if (!Number.isNaN(Number(value))) 19 | return `${value}${unit}` 20 | 21 | return undefined 22 | } 23 | 24 | export function rule(prefix: string, 25 | property: string, 26 | options?: { 27 | defaultUnit?: string 28 | postprocess?: (size: string) => string 29 | }, 30 | ): DynamicRule { 31 | return [ 32 | new RegExp(`^${prefix}-(.+)$`), 33 | ([_, value], { theme }) => { 34 | const unit = options?.defaultUnit ?? 'rem' 35 | 36 | // Try bracket 37 | const unbracket = (h.bracket(value)) 38 | if (unbracket !== undefined) { 39 | // return early 40 | // we do not apply postprocess to brackets 41 | return { [property]: unbracket } 42 | } 43 | 44 | // Not a bracket, let's try to resolve 45 | let resolvedValue = resolve(value, theme, unit) 46 | 47 | // Failed to resolve 48 | if (resolvedValue === undefined) 49 | return undefined 50 | 51 | // If we got here resolution must have succeeded, 52 | // let's apply the postprocess now 53 | if (options?.postprocess) 54 | resolvedValue = options.postprocess(resolvedValue) 55 | 56 | return { [property]: resolvedValue } 57 | }, 58 | ] 59 | } 60 | 61 | export function axisRules(prefix: string, postfix: string, propertyPrefix: string, propertyPostfix: string) { 62 | return logical.axisRules(prefix, postfix, propertyPrefix, propertyPostfix, rule) 63 | } 64 | 65 | export function edgeRules(prefix: string, postfix: string, propertyPrefix: string, propertyPostfix: string) { 66 | return logical.edgeRules(prefix, postfix, propertyPrefix, propertyPostfix, rule) 67 | } 68 | 69 | export function cornerRules(prefix: string, postfix: string, propertyPrefix: string, propertyPostfix: string) { 70 | return logical.cornerRules(prefix, postfix, propertyPrefix, propertyPostfix, rule) 71 | } 72 | -------------------------------------------------------------------------------- /packages/core/src/rule-utils/time.ts: -------------------------------------------------------------------------------- 1 | import type { DynamicRule } from '@unocss/core' 2 | import type Theme from '../theme/Theme' 3 | 4 | function durationRule(prefix: string, 5 | property: string, 6 | value?: (size: string) => string, 7 | ): DynamicRule { 8 | return [ 9 | new RegExp(`^(${prefix})-(.+)$`), 10 | (match, { theme }) => { 11 | if (!theme.windblade) 12 | return 13 | 14 | const css: any = {} 15 | const parameter = `${theme.windblade.proportions[match[2]] * theme.windblade.time.baseUnitMs}ms` 16 | if (parameter === undefined) 17 | return undefined 18 | css[property] = value?.(parameter) ?? parameter 19 | 20 | return css 21 | }, 22 | ] 23 | } 24 | 25 | function timingFunctionRule( 26 | prefix: string, 27 | property: string, 28 | ): DynamicRule { 29 | return [ 30 | new RegExp(`^(${prefix})-(.+)$`), 31 | (match, { theme }) => { 32 | if (!theme.windblade) 33 | return 34 | 35 | const css: any = {} 36 | const parameter = theme.windblade.time.functions[match[2]] 37 | if (parameter === undefined) 38 | return undefined 39 | css[property] = parameter 40 | return css 41 | }, 42 | ] 43 | } 44 | 45 | export { durationRule, timingFunctionRule } 46 | -------------------------------------------------------------------------------- /packages/core/src/theme/Theme/ThemeColors.ts: -------------------------------------------------------------------------------- 1 | export interface ThemeColor { 2 | dark: { 3 | l: number 4 | c: number 5 | a?: number 6 | } 7 | light?: { 8 | l?: number 9 | c?: number 10 | a?: number 11 | } 12 | } 13 | 14 | export interface ThemeColorCombo { 15 | base: ThemeColor 16 | on: ThemeColor[] 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/theme/Theme/index.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeColorCombo } from './ThemeColors' 2 | 3 | export interface Theme { 4 | windblade?: { 5 | colors: Record 6 | miscColors?: Record 7 | proportions: Record 8 | miscSizes?: Record 9 | time: { 10 | baseUnitMs: number 11 | functions: Record & { 12 | default: string 13 | } 14 | } 15 | } 16 | } 17 | 18 | export default Theme 19 | export * from './ThemeColors' 20 | -------------------------------------------------------------------------------- /packages/core/src/theme/index.ts: -------------------------------------------------------------------------------- 1 | import type Theme from './Theme' 2 | 3 | const theme: Theme = { 4 | windblade: { 5 | colors: {}, 6 | 7 | miscColors: { 8 | transparent: 'transparent', 9 | inherit: 'inherit', 10 | currentColor: 'currentColor', 11 | }, 12 | 13 | proportions: {}, 14 | 15 | miscSizes: { 16 | 0: '0px', 17 | px: '1px', 18 | half: '50%', 19 | full: '100%', 20 | auto: 'auto', 21 | min: 'min-content', 22 | max: 'max-content', 23 | fit: 'fit-content', 24 | }, 25 | 26 | time: { 27 | baseUnitMs: 0, 28 | functions: { 29 | default: 'linear', 30 | }, 31 | }, 32 | }, 33 | } 34 | 35 | export default theme 36 | export * from './Theme' 37 | -------------------------------------------------------------------------------- /packages/core/src/themes/index.ts: -------------------------------------------------------------------------------- 1 | import material3 from './material3' 2 | import tailwind from './tailwind' 3 | import windblade from './windblade' 4 | 5 | export const themes = { 6 | none: {}, 7 | material3, 8 | tailwind, 9 | windblade, 10 | } as const 11 | 12 | export type WindbladeTheme = keyof typeof themes 13 | -------------------------------------------------------------------------------- /packages/core/src/themes/material3.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@windblade/core' 2 | 3 | const main: Theme = { 4 | windblade: { 5 | colors: { 6 | // TODO: Fill this 7 | }, 8 | 9 | proportions: { 10 | extrasmall: 0.25, 11 | small: 0.5, 12 | medium: 0.75, 13 | large: 1, 14 | extralarge: 1.75, 15 | }, 16 | 17 | time: { 18 | baseUnitMs: 150, 19 | 20 | functions: { 21 | default: 'cubic-bezier(0.4, 0, 0.2, 1)', 22 | 23 | // TODO: Fill this 24 | }, 25 | }, 26 | }, 27 | } 28 | 29 | export default main 30 | -------------------------------------------------------------------------------- /packages/core/src/themes/tailwind.ts: -------------------------------------------------------------------------------- 1 | import type { Theme, ThemeColorCombo } from '@windblade/core' 2 | 3 | function tailwindColorSet(name: string, colors: { l: number; c: number }[]): Record { 4 | return { 5 | [`${name}-900`]: { 6 | base: { dark: { l: colors[0].l, c: colors[0].c }, light: { l: colors[9].l, c: colors[9].c } }, 7 | on: [{ dark: { l: 1, c: 0 } }], 8 | }, 9 | [`${name}-800`]: { 10 | base: { dark: { l: colors[1].l, c: colors[1].c }, light: { l: colors[8].l, c: colors[8].c } }, 11 | on: [{ dark: { l: 1, c: 0 } }], 12 | }, 13 | [`${name}-700`]: { 14 | base: { dark: { l: colors[2].l, c: colors[2].c }, light: { l: colors[7].l, c: colors[7].c } }, 15 | on: [{ dark: { l: 1, c: 0 } }], 16 | }, 17 | [`${name}-600`]: { 18 | base: { dark: { l: colors[3].l, c: colors[3].c }, light: { l: colors[6].l, c: colors[6].c } }, 19 | on: [{ dark: { l: 1, c: 0 } }], 20 | }, 21 | [`${name}-500`]: { 22 | base: { dark: { l: colors[4].l, c: colors[4].c }, light: { l: colors[5].l, c: colors[5].c } }, 23 | on: [{ dark: { l: 1, c: 0 } }], 24 | }, 25 | [`${name}-400`]: { 26 | base: { dark: { l: colors[5].l, c: colors[5].c }, light: { l: colors[4].l, c: colors[4].c } }, 27 | on: [{ dark: { l: 0, c: 0 } }], 28 | }, 29 | [`${name}-300`]: { 30 | base: { dark: { l: colors[6].l, c: colors[6].c }, light: { l: colors[3].l, c: colors[3].c } }, 31 | on: [{ dark: { l: 0, c: 0 } }], 32 | }, 33 | [`${name}-200`]: { 34 | base: { dark: { l: colors[7].l, c: colors[7].c }, light: { l: colors[2].l, c: colors[2].c } }, 35 | on: [{ dark: { l: 0, c: 0 } }], 36 | }, 37 | [`${name}-100`]: { 38 | base: { dark: { l: colors[8].l, c: colors[8].c }, light: { l: colors[1].l, c: colors[1].c } }, 39 | on: [{ dark: { l: 0, c: 0 } }], 40 | }, 41 | [`${name}-50`]: { 42 | base: { dark: { l: colors[9].l, c: colors[9].c }, light: { l: colors[0].l, c: colors[0].c } }, 43 | on: [{ dark: { l: 0, c: 0 } }], 44 | }, 45 | } 46 | } 47 | 48 | const main: Theme = { 49 | windblade: { 50 | colors: { 51 | absolute: { 52 | base: { dark: { l: 0, c: 0 } }, 53 | on: [{ dark: { l: 1, c: 0 } }], 54 | }, 55 | 56 | ...tailwindColorSet('neutral', [ 57 | { l: 0.21, c: 0 }, 58 | { l: 0.28, c: 0 }, 59 | { l: 0.37, c: 0 }, 60 | { l: 0.45, c: 0 }, 61 | { l: 0.55, c: 0 }, 62 | { l: 0.71, c: 0 }, 63 | { l: 0.87, c: 0 }, 64 | { l: 0.92, c: 0 }, 65 | { l: 0.97, c: 0 }, 66 | { l: 0.98, c: 0 }, 67 | ]), 68 | 69 | ...tailwindColorSet('gray', [ 70 | { l: 0.22, c: 0.023 }, 71 | { l: 0.28, c: 0.026 }, 72 | { l: 0.37, c: 0.03 }, 73 | { l: 0.45, c: 0.026 }, 74 | { l: 0.55, c: 0.023 }, 75 | { l: 0.71, c: 0.019 }, 76 | { l: 0.87, c: 0.009 }, 77 | { l: 0.92, c: 0.006 }, 78 | { l: 0.97, c: 0.003 }, 79 | { l: 0.98, c: 0.002 }, 80 | ]), 81 | 82 | ...tailwindColorSet('slate', [ 83 | { l: 0.21, c: 0.035 }, 84 | { l: 0.28, c: 0.035 }, 85 | { l: 0.37, c: 0.039 }, 86 | { l: 0.45, c: 0.037 }, 87 | { l: 0.55, c: 0.041 }, 88 | { l: 0.71, c: 0.035 }, 89 | { l: 0.87, c: 0.02 }, 90 | { l: 0.92, c: 0.013 }, 91 | { l: 0.97, c: 0.007 }, 92 | { l: 0.98, c: 0.003 }, 93 | ]), 94 | 95 | ...tailwindColorSet('strong', [ 96 | { l: 0.35, c: 0.16 }, 97 | { l: 0.4, c: 0.2 }, 98 | { l: 0.50, c: 0.24 }, 99 | { l: 0.60, c: 0.2 }, 100 | { l: 0.70, c: 0.16 }, 101 | { l: 0.75, c: 0.13 }, 102 | { l: 0.81, c: 0.1 }, 103 | { l: 0.87, c: 0.06 }, 104 | { l: 0.92, c: 0.03 }, 105 | { l: 0.97, c: 0.02 }, 106 | ]), 107 | }, 108 | 109 | proportions: { 110 | '0.5': 0.125, 111 | '1': 0.25, 112 | '1.5': 0.375, 113 | '2': 0.5, 114 | '2.5': 0.625, 115 | '3': 0.75, 116 | '3.5': 0.875, 117 | '4': 1, 118 | '5': 1.25, 119 | '6': 1.5, 120 | '7': 1.75, 121 | '8': 2, 122 | '9': 2.25, 123 | '10': 2.5, 124 | '11': 2.75, 125 | '12': 3, 126 | '14': 3.5, 127 | '16': 4, 128 | '20': 5, 129 | '24': 6, 130 | '28': 7, 131 | '32': 8, 132 | '36': 9, 133 | '40': 10, 134 | '44': 11, 135 | '48': 12, 136 | '52': 13, 137 | '56': 14, 138 | '60': 15, 139 | '64': 16, 140 | '72': 18, 141 | '80': 20, 142 | '96': 24, 143 | 'sm': 40, 144 | 'md': 48, 145 | 'lg': 64, 146 | 'xl': 80, 147 | '2xl': 96, 148 | }, 149 | 150 | time: { 151 | baseUnitMs: 150, 152 | 153 | functions: { 154 | 'default': 'cubic-bezier(0.4, 0, 0.2, 1)', 155 | 156 | 'linear': 'linear', 157 | 'in': 'cubic-bezier(0.4, 0, 1, 1)', 158 | 'out': 'cubic-bezier(0, 0, 0.2, 1)', 159 | 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)', 160 | }, 161 | }, 162 | }, 163 | } 164 | 165 | export default main 166 | -------------------------------------------------------------------------------- /packages/core/src/themes/windblade.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@windblade/core' 2 | 3 | const main: Theme = { 4 | windblade: { 5 | colors: {}, 6 | 7 | proportions: { 8 | 'zero': 0, 9 | 's.2': 0.2, 10 | 's.4': 0.4, 11 | 's.6': 0.6, 12 | 's.8': 0.8, 13 | 's': 1, 14 | 'm.2': 2, 15 | 'm.4': 4, 16 | 'm.6': 6, 17 | 'm.8': 8, 18 | 'm': 10, 19 | 'l.2': 20, 20 | 'l.4': 40, 21 | 'l.6': 60, 22 | 'l.8': 80, 23 | 'l': 100, 24 | }, 25 | 26 | time: { 27 | baseUnitMs: 150, 28 | 29 | functions: { 30 | 'default': 'cubic-bezier(0.4, 0, 0.2, 1)', 31 | 32 | 'linear': 'linear', 33 | 'in': 'cubic-bezier(0.4, 0, 1, 1)', 34 | 'out': 'cubic-bezier(0, 0, 0.2, 1)', 35 | 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)', 36 | }, 37 | }, 38 | }, 39 | } 40 | 41 | export default main 42 | -------------------------------------------------------------------------------- /packages/core/src/utils/color.ts: -------------------------------------------------------------------------------- 1 | import convert from 'better-color-tools' 2 | import type { ThemeColor } from '../theme/Theme' 3 | import type Theme from '../theme/Theme' 4 | 5 | export type ColorScheme = 'dark' | 'light' 6 | 7 | export function getLCA(color: ThemeColor): Record { 8 | const l = color.dark.l 9 | const c = color.dark.c 10 | const a = color.dark.a ?? 1 11 | 12 | return { 13 | dark: { l, c, a }, 14 | light: { 15 | l: color.light?.l ?? 1 - l, 16 | c: color.light?.c ?? c, 17 | a: color.light?.a ?? a, 18 | }, 19 | } 20 | } 21 | 22 | export function getLCHA(hue: number, color: ThemeColor): Record { 23 | const sla: any = getLCA(color) 24 | Object.assign(sla, { 25 | dark: { h: hue }, 26 | light: { h: hue }, 27 | }) 28 | 29 | return sla 30 | } 31 | 32 | export const LCHToCSSColor = (l: number, c: number, h: number, a = 1) => convert.from(`oklch(${l} ${c}, ${h}, ${a})`) 33 | 34 | export type ColorSchemeProps = Record> 35 | 36 | export function getColorSchemeCSSProps(theme: Theme, hue: number): ColorSchemeProps { 37 | const colorSchemeProps: ColorSchemeProps = { 38 | light: {}, 39 | dark: {}, 40 | } 41 | 42 | if (!theme.windblade) 43 | return colorSchemeProps 44 | 45 | const { colors: colorCombos } = theme.windblade 46 | 47 | // Iterate over color combos 48 | Object.entries(colorCombos).forEach(([colorComboName, colorCombo]) => { 49 | // Collect 'base' and 'on' colors into single array 50 | const colors = [ 51 | colorCombo.base, 52 | ...colorCombo.on, 53 | ] 54 | 55 | // Iterate over collected colors 56 | colors.forEach((color, i) => { 57 | // --NAME-0 is base 58 | // --NAME-1..inf is on 59 | const propName = `--${colorComboName}-${i === 0 ? 'base' : `fg-${i}`}` 60 | 61 | const { dark: lcaDark, light: lcaLight } = getLCA(color) 62 | 63 | colorSchemeProps.dark[propName] = convert.from(`oklch(${lcaDark.l} ${lcaDark.c}, ${hue}, ${lcaDark.a})`).rgba 64 | colorSchemeProps.light[propName] = convert.from(`oklch(${lcaLight.l} ${lcaLight.c}, ${hue}, ${lcaLight.a})`).rgba 65 | }) 66 | }) 67 | 68 | return colorSchemeProps 69 | } 70 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | function objToCSS(obj: Record) { 2 | let css = '' 3 | Object.keys(obj).forEach((key) => { 4 | css += `${key}: ${obj[key]};\n` 5 | }) 6 | return css 7 | } 8 | 9 | export { objToCSS } 10 | export * from './color' 11 | -------------------------------------------------------------------------------- /packages/unocss-docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | -------------------------------------------------------------------------------- /packages/unocss-docs/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | 'src/index', 6 | ], 7 | clean: true, 8 | declaration: true, 9 | externals: [], 10 | rollup: { 11 | emitCJS: true, 12 | dts: { 13 | respectExternal: false, 14 | }, 15 | }, 16 | }) 17 | -------------------------------------------------------------------------------- /packages/unocss-docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/unocss-docs", 3 | "version": "2.0.0-beta.7", 4 | "exports": { 5 | ".": { 6 | "types": "./dist/index.d.ts", 7 | "require": "./dist/index.cjs", 8 | "import": "./dist/index.mjs" 9 | }, 10 | "./*": "./*" 11 | }, 12 | "main": "dist/index.cjs", 13 | "module": "dist/index.mjs", 14 | "types": "dist/index.d.ts", 15 | "files": [ 16 | "dist", 17 | "*.css" 18 | ], 19 | "scripts": { 20 | "build": "unbuild", 21 | "stub": "unbuild --stub" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/unocss-docs/src/index.ts: -------------------------------------------------------------------------------- 1 | export function encodeString(str: string) { 2 | return str.replace(/&/g, '&') 3 | .trimStart() 4 | .replace(//g, '>') 6 | .replace(/"/g, '"') 7 | .replace(/'/g, ''') 8 | .replace(/\n/g, ' ') 9 | } 10 | 11 | export * from './types' 12 | -------------------------------------------------------------------------------- /packages/unocss-docs/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Md = string 2 | 3 | export type DocumentationPage = Md 4 | 5 | export type DocumentationTree = Map 6 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/unocss-preset-color", 3 | "version": "2.0.0-beta.7", 4 | "description": "", 5 | "exports": { 6 | ".": { 7 | "types": "./dist/index.d.ts", 8 | "require": "./dist/index.cjs", 9 | "import": "./dist/index.mjs" 10 | }, 11 | "./*": "./*" 12 | }, 13 | "main": "dist/index.cjs", 14 | "module": "dist/index.mjs", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist", 18 | "*.css" 19 | ], 20 | "scripts": { 21 | "build": "unbuild", 22 | "stub": "unbuild --stub" 23 | }, 24 | "dependencies": { 25 | "@unocss/core": "0.55.0", 26 | "@windblade/core": "workspace:*", 27 | "@windblade/unocss-docs": "workspace:*", 28 | "ts-extras": "^0.11.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import usage from './usage' 3 | import rules from './rules' 4 | 5 | const main: DocumentationTree = new Map([ 6 | ['Usage', usage], 7 | ...rules, 8 | ]) 9 | 10 | export default main 11 | export * as usage from './usage' 12 | export * as rules from './rules' 13 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/rules.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | 3 | import * as backgrounds from '../rules/documented/backgrounds' 4 | import * as typography from '../rules/documented/typography' 5 | import * as borders from '../rules/documented/borders' 6 | import * as interactivity from '../rules/documented/interactivity' 7 | import * as svg from '../rules/documented/svg' 8 | import * as accessibility from '../rules/documented/accessibility' 9 | 10 | const main: DocumentationTree = new Map([ 11 | ['Background', new Map([ 12 | ['Background Color', backgrounds.bgColor().docs], 13 | ['Background Gradient', backgrounds.backgroundImage().docs], 14 | ['Background Gradient Stops', backgrounds.gradientColorStops().docs], 15 | ])], 16 | ['Text', new Map([ 17 | ['Text Color', typography.textColor().docs], 18 | ['Text Decoration Color', typography.textDecorationColor().docs], 19 | ])], 20 | ['Border', new Map([ 21 | ['Border Color', borders.borderColor().docs], 22 | ['Outline Color', borders.outlineColor().docs], 23 | ])], 24 | ['Other', new Map([ 25 | ['Color Scheme', accessibility.colorScheme().docs], 26 | 27 | ['Accent color', interactivity.accentColor().docs], 28 | ['Caret color', interactivity.caretColor().docs], 29 | 30 | ['Fill', svg.fill().docs], 31 | ['Stroke Color', svg.stroke().docs], 32 | ])], 33 | ]) 34 | 35 | export default main 36 | export { 37 | backgrounds, 38 | borders, 39 | interactivity, 40 | svg, 41 | typography, 42 | accessibility, 43 | } 44 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/usage/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import installation from './installation' 3 | import theme from './theme' 4 | import semanticColors from './semanticColors' 5 | 6 | export const categoy: DocumentationTree = new Map([ 7 | ['Installation', installation], 8 | ['Theme', theme], 9 | ['Semantic colors', semanticColors], 10 | ]) 11 | 12 | export { 13 | installation, 14 | theme, 15 | semanticColors, 16 | } 17 | 18 | export default categoy 19 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/usage/installation.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example = ` 5 | import { defineConfig } from 'unocss'; 6 | import windbladeColor from '@windblade/unocss-preset-color'; 7 | 8 | export default defineConfig({ 9 | presets: [ 10 | windbladeColor(), 11 | ], 12 | });` 13 | 14 | const removeColors = `import { defineConfig, presetMini } from 'unocss'; 15 | import windbladeColor from '@windblade/unocss-preset-color'; 16 | 17 | export default defineConfig({ 18 | extendTheme: [ 19 | (theme) => { 20 | delete theme.colors 21 | }, 22 | ], 23 | presets: [ 24 | windbladeColor(), 25 | presetMini(), 26 | ], 27 | });` 28 | 29 | const main: DocumentationPage = ` 30 | 31 |

</h1> 32 | <p>Just like the complete Windblade, the Color module is an UnoCSS preset, please follow its own <a href="https://unocss.dev/integrations">guide</a> to install it.</p> 33 | <p>Once UnoCSS is installed in your project simply get @windblade/unocss-preset-color from npm and add it to the presets array.</p> 34 | <pre lang="sh" code="npm install @windblade/unocss-preset-color" /> 35 | <pre lang="ts" code="${encodeString(example)}" /> 36 | <p>That's it! You can now use Windblade's color utilities in your proejct but it is likely that you want more utilities for things like layout and typography, please read on to learn how to use Windblade's color module othether with other UnoCSS presets.</p> 37 | 38 | <h2>Installation with other UnoCSS presets</h2> 39 | <p>You likely want to combine @windblade/unocss-preset-color with other UnoCSS presets (e.g. @unocss/preset-mini) to get utilities for layout, typography and other CSS features. This should generally work without any problems but we recommend removing colors from other presets' configuration to avoid confusion and possible conflicts.</p> 40 | <p>You will find instructions on how to remove colors from most popuilar UnoCSS presets below. If you are using other presets consult the docs for those presets.</p> 41 | 42 | <h3>@unocss/preset-mini, @unocss/preset-wind & @unocss/preset-uno</h3> 43 | <p>Use <code>extendTheme</code> property to remove <code>colors</code> from the config.</p> 44 | <pre lang="ts" code="${encodeString(removeColors)}" /> 45 | </page> 46 | ` 47 | 48 | export default main 49 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/usage/semanticColors.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example1 = ` 5 | <div class="bg-normal"> 6 | This will have the 'normal' background 7 | <div class="bg-accent"> And this will have the 'accent' background </div> 8 | </div> 9 | ` 10 | 11 | const example2 = ` 12 | <div class="bg-normal"> 13 | This will have the 'normal' background and default foreground color. 14 | <span class="text-fg-2"> And this will have the secondary foreground color </span> 15 | <div class="bg-fg-2"> <!-- This div has secondary foreground color as background --> </div> 16 | </div> 17 | ` 18 | const example3 = ` 19 | <body class="scheme-auto-80 bg-normal"> 20 | ... 21 | </body> 22 | ` 23 | 24 | const example5 = ` 25 | import { utils } from "@windblade/core"; 26 | import { theme } from "@windblade/unocss-preset"; // this is just a source file and it does not know about your theme customizations. If you are using your own colors you should import them instead 27 | 28 | const { getColorSchemeCSSProps, objToCSS } = utils 29 | 30 | const brandHue = 80; 31 | 32 | getBrandColor((light?: boolean) => { 33 | const colors = getLCA(theme.windblade.colors['brand'].base); // returns light and dark variants with all values calculated 34 | 35 | let lca; 36 | if (light) { 37 | lca = colors.light; 38 | } else { 39 | lca = colors.dark; 40 | } 41 | 42 | return LCHToCSSColor(lca.l, lca.c, brandHue, lca.a).rgba; 43 | }); 44 | 45 | export default getBrandColor;` 46 | 47 | const main: DocumentationPage = ` 48 | <page> 49 | <h1>Using semantic colors</h1> 50 | <p>Windblade comes with a semantic color system. Refer to colors by their names.</p> 51 | <pre lang="html" code="${encodeString(example1)}" /> 52 | 53 | <h2>Foreground colors</h2> 54 | <p>All colors have one or more foreground colors. The first foreground color is set as CSS color automatically but you can override it with others or use it for other properties. The foreground colors are updated whenever the bg utility is applied.</p> 55 | <pre lang="html" code="${encodeString(example2)}" /> 56 | 57 | <h2>Color scheme</h2> 58 | <p>Change hue and color scheme by applying color rules together with or inside of sheme-(auto|light|dark)-{number}.</p> 59 | <pre lang="html" code="${encodeString('<div class="scheme-auto-80 bg-normal"></div>')}" /> 60 | 61 | <p>It is a good idea apply a color scheme at the root of your app.</p> 62 | <pre lang="html" code="${encodeString(example3)}" /> 63 | 64 | <h2>Using Windblade colors in JavaScrtipt</h2> 65 | <p>Sometimes you might need to set a color with JavaScript and you might be unable to use a class (e.g. drawing to a canvas). In those situations, you can use Windblade's core module.</p> 66 | <pre lang="ts" code="${encodeString(example5)}" /> 67 | </page> 68 | ` 69 | 70 | export default main 71 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/docs/usage/theme.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | import type { ThemeColorCombo } from '@windblade/core' 4 | import theme from '../../theme' 5 | 6 | const wb = theme.windblade 7 | 8 | const example = ` 9 | const theme: Theme = { 10 | windblade: { 11 | colors: { 12 | 'mycolor': { 13 | base: { dark: { l: 0, c: 0 } }, 14 | on: [ 15 | { dark: { l: 1, c: 0 } }, 16 | ], 17 | }, 18 | }, 19 | }, 20 | };` 21 | 22 | const example2 = ` 23 | const theme: Theme = { 24 | windblade: { 25 | colors: { 26 | 'myColor': { 27 | base: { dark: { l: 0, c: 0 }, light: { l: 0.9, c: 0.05 } }, // slightly more saturated and darker than it would be otherwise in light mode (1 - 0 = 1, we tell it to be 0.9) 28 | on: [ 29 | { dark: { l: 1, c: 0 }, light: { a: 0.8 } }, // more transparent in light mode (80%) 30 | ], 31 | }, 32 | 'brandColor': { 33 | base: { dark: { l: 0.6, c: 0.3 }, light: { l: 0.6 } }, // has lightness 0.6 in both color schemes 34 | on: [ 35 | { dark: { l: 0, c: 0 } }, 36 | ], 37 | }, 38 | }, 39 | }, 40 | };` 41 | 42 | const colors: DocumentationPage = ` 43 | <page> 44 | <h1><title /></h1> 45 | <p>Colors in Windblade are based on the <a href="https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl">OkLCH</a> model and have a 'base' and one or more 'on' colors.</p> 46 | 47 | <h2>Default colors</h2> 48 | <p>It is highly encouraged that you use your own colors, however, Windblade does come with a set of well-crafted example colors that are designed to demonstrate the semantic color system and were used to build this documnentation.</p> 49 | <example html="${encodeString(` 50 | <div class="grid grid-fit-cols-m gap-s.4"> 51 | ${((): string => { 52 | const colors = wb?.colors 53 | if (typeof colors === 'object') { 54 | return Object.entries(colors as Record<string, ThemeColorCombo>).map(([name, colorCombo]) => ` 55 | <div class="bg-${name} flex flex-col border border-color-surface rounded-s overflow-hidden font-bold"> 56 | <h1 class="p-s">${name}</h1> 57 | <div class="size-b-px shrink-0 bg-fg-1 opacity-[0.1]"></div> 58 | <div class="size-b-full flex flex-col gap-s p-s"> 59 | ${colorCombo.on.map((_, i) => `<div class="text-fg-${i + 1}">Fg-${i + 1}</div>`).join('')} 60 | </div> 61 | </div> 62 | `).join('') 63 | } 64 | return 'Error: windblade.colors is not an object.' 65 | })()} 66 | </div> 67 | `)}" /> 68 | 69 | <h2>Custom colors</h2> 70 | <p>Add a color by specifying an object like the following:</p> 71 | <pre lang="ts" code="${encodeString(example)}" /> 72 | 73 | <p>Dark scheme is the default and, unless overriden, light scheme is generated automatically by flipping the lightness value. That is great for effortlessly prototyping a light mode but you will often want more than simple lightness fliping. In those cases you can override any light mode component of the color manually. Default windblade colors use this to increase contrast and saturation in light mode.</p> 74 | <pre lang="ts" code="${encodeString(example2)}" /> 75 | </page> 76 | ` 77 | 78 | export default colors 79 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Preset } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | import theme from './theme' 4 | import rules from './rules' 5 | 6 | function main(options: {} = {}): Preset<Theme> { 7 | return { 8 | name: '@windblade/unocss-preset-color', 9 | theme, 10 | rules, 11 | options, 12 | prefix: undefined, 13 | } 14 | } 15 | 16 | export default main 17 | export * as docs from './docs' 18 | export { theme, rules } 19 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/documented/accessibility.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import { escapeSelector as e } from '@unocss/core' 3 | import type { DocumentationPage } from '@windblade/unocss-docs' 4 | import { encodeString } from '@windblade/unocss-docs' 5 | import type { Theme } from '@windblade/core' 6 | import { utils } from '@windblade/core' 7 | 8 | const { getColorSchemeCSSProps, objToCSS } = utils 9 | 10 | export function colorScheme() { 11 | const rules: Rule<Theme>[] = [ 12 | [ 13 | /^scheme-(auto|light|dark)-(\d+)$/, 14 | (match, { rawSelector, theme }) => { 15 | const hue = Number(match[2] ?? 0) 16 | if (Number.isNaN(hue)) 17 | return 18 | 19 | const selector = e(rawSelector) 20 | const { dark, light } = getColorSchemeCSSProps(theme, hue) 21 | 22 | switch (match[1]) { 23 | case 'light': 24 | return ` 25 | .${selector} { 26 | color-scheme: light; 27 | ${objToCSS(light)} 28 | } 29 | ` 30 | case 'dark': 31 | return ` 32 | .${selector} { 33 | color-scheme: dark; 34 | ${objToCSS(dark)} 35 | } 36 | ` 37 | 38 | default: 39 | return ` 40 | .${selector} { 41 | color-scheme: light dark; 42 | ${objToCSS(dark)} 43 | } 44 | 45 | @media (prefers-color-scheme: light) { .${selector} { 46 | ${objToCSS(light)} 47 | } 48 | } 49 | ` 50 | } 51 | }, 52 | ], 53 | ] 54 | 55 | const docs: DocumentationPage = ` 56 | <page> 57 | <h1><title /></h1> 58 | <p>Utilities for switching color scheme. Missing from Tailwind.</p> 59 | 60 | <h2>Try it</h2> 61 | <try-it> 62 | <utils> 63 | <util> 64 | scheme- 65 | <select> 66 | <option value="auto" /> 67 | <option value="dark" /> 68 | <option value="light" /> 69 | </select> 70 | - 71 | <input type="integer" /> 72 | </util> 73 | </utils> 74 | 75 | <renderer html="${encodeString(` 76 | <div class="$util bg-normal p-m rounded-s"> 77 | <div class="bg-surface border border-color-surface p-s rounded-s flex gap-s items-center"> 78 | <div class="size-i-m.2 aspect-1/1 bg-accent rounded-full"></div> 79 | <div>Hello</div> 80 | </div> 81 | </div> 82 | `)}" /> 83 | 84 | 85 | <h3>Preview</h3> 86 | <viewport /> 87 | 88 | <h3>HTML</h3> 89 | <html /> 90 | 91 | <h3>Generated CSS</h3> 92 | <css /> 93 | </try-it> 94 | </page> 95 | ` 96 | 97 | return { rules, docs } 98 | } 99 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/documented/borders.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { color, logical } = ruleUtils 8 | 9 | export function borderColor() { 10 | const rules: Rule<Theme>[] = [ 11 | ...logical.edgeRules('border', 'color', 'border', 'color', color.colorRule), 12 | ...logical.edgeRules('border', 'color-fg', 'border', 'color', color.fgColorRule), 13 | ] 14 | 15 | const docs: DocumentationPage = ` 16 | <page> 17 | <h1><title /></h1> 18 | <p>Windblade uses semantic colors.</p> 19 | 20 | <h2>Try it</h2> 21 | <try-it selected="$util"> 22 | <utils> 23 | <util> 24 | border-color- 25 | <select> 26 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 27 | <option value="$name"/> 28 | </for> 29 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 30 | <option value="$name"/> 31 | </for> 32 | </select> 33 | </util> 34 | <util> 35 | border-color-fg- 36 | <input type="integer" /> 37 | </util> 38 | <util> 39 | border- 40 | <select> 41 | ${Object.keys(logical.abbreviations.axis).map(val => `<option value="${val}" />`)} 42 | ${Object.keys(logical.abbreviations.edges).map(val => `<option value="${val}" />`)} 43 | </select> 44 | -color- 45 | <select> 46 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 47 | <option value="$name"/> 48 | </for> 49 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 50 | <option value="$name"/> 51 | </for> 52 | </select> 53 | </util> 54 | <util> 55 | border- 56 | <select> 57 | ${Object.keys(logical.abbreviations.axis).map(val => `<option value="${val}" />`)} 58 | ${Object.keys(logical.abbreviations.edges).map(val => `<option value="${val}" />`)} 59 | </select> 60 | -color-fg- 61 | <input type="integer" /> 62 | </util> 63 | </utils> 64 | 65 | <renderer html="${encodeString(` 66 | <div class="border border-width-s.2 $util rounded-s size-i-full max-size-i-l.2 aspect-1/1"></div> 67 | `)}" /> 68 | 69 | <h3>Preview</h3> 70 | <viewport /> 71 | 72 | <h3>HTML</h3> 73 | <html /> 74 | 75 | <h3>Generated CSS</h3> 76 | <css /> 77 | </try-it> 78 | </page> 79 | ` 80 | 81 | return { rules, docs } 82 | } 83 | 84 | export function outlineColor() { 85 | const rules: Rule<Theme>[] = [ 86 | color.colorRule('outline', 'outline-color'), 87 | color.fgColorRule('outline-fg', 'outline-color'), 88 | ] 89 | 90 | const docs: DocumentationPage = ` 91 | <page> 92 | <h1><title /></h1> 93 | <p>Windblade uses semantic colors.</p> 94 | 95 | <h2>Try it</h2> 96 | <try-it selected="$util"> 97 | <utils> 98 | <util> 99 | outline-color- 100 | <select> 101 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 102 | <option value="$name"/> 103 | </for> 104 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 105 | <option value="$name"/> 106 | </for> 107 | </select> 108 | </util> 109 | <util renderer="fg"> 110 | outline-color-fg- 111 | <input type="integer" /> 112 | </util> 113 | </utils> 114 | 115 | <renderer html="${encodeString(` 116 | TODO 117 | `)}" /> 118 | 119 | <h3>Preview</h3> 120 | <viewport /> 121 | 122 | <h3>HTML</h3> 123 | <html /> 124 | 125 | <h3>Generated CSS</h3> 126 | <css /> 127 | </try-it> 128 | </page> 129 | ` 130 | 131 | return { rules, docs } 132 | } 133 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/documented/interactivity.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { color } = ruleUtils 8 | 9 | export function accentColor() { 10 | const rules: Rule<Theme>[] = [color.colorRule('accent', 'accent-color')] 11 | 12 | const docs: DocumentationPage = ` 13 | <page> 14 | <h1><title /></h1> 15 | <p>Windblade uses semantic colors.</p> 16 | 17 | <h2>Try it</h2> 18 | <try-it selected="$util"> 19 | <utils> 20 | <util> 21 | accent- 22 | <select> 23 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 24 | <option value="$name"/> 25 | </for> 26 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 27 | <option value="$name"/> 28 | </for> 29 | </select> 30 | </util> 31 | <util renderer="fg"> 32 | accent-fg- 33 | <input type="integer" /> 34 | </util> 35 | </utils> 36 | 37 | <renderer html="${encodeString(` 38 | TODO 39 | `)}" /> 40 | 41 | <h3>Preview</h3> 42 | <viewport /> 43 | 44 | <h3>HTML</h3> 45 | <html /> 46 | 47 | <h3>Generated CSS</h3> 48 | <css /> 49 | </try-it> 50 | </page> 51 | ` 52 | 53 | return { rules, docs } 54 | } 55 | 56 | export function caretColor() { 57 | const rules: Rule<Theme>[] = [ 58 | color.colorRule('caret', 'caret-color'), 59 | color.fgColorRule('caret-fg', 'caret-color'), 60 | ] 61 | 62 | const docs: DocumentationPage = ` 63 | <page> 64 | <h1><title /></h1> 65 | <p>Windblade uses semantic colors.</p> 66 | 67 | <h2>Try it</h2> 68 | <try-it selected="$util"> 69 | <utils> 70 | <util> 71 | caret- 72 | <select> 73 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 74 | <option value="$name"/> 75 | </for> 76 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 77 | <option value="$name"/> 78 | </for> 79 | </select> 80 | </util> 81 | <util renderer="fg"> 82 | caret-fg- 83 | <input type="integer" /> 84 | </util> 85 | </utils> 86 | 87 | <renderer html="${encodeString(` 88 | TODO 89 | `)}" /> 90 | 91 | <h3>Preview</h3> 92 | <viewport /> 93 | 94 | <h3>HTML</h3> 95 | <html /> 96 | 97 | <h3>Generated CSS</h3> 98 | <css /> 99 | </try-it> 100 | </page> 101 | ` 102 | 103 | return { rules, docs } 104 | } 105 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/documented/svg.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { color } = ruleUtils 8 | 9 | export function fill() { 10 | const rules: Rule<Theme>[] = [ 11 | color.colorRule('fill', 'fill'), 12 | color.fgColorRule('fill-fg', 'fill'), 13 | ] 14 | 15 | const docs: DocumentationPage = ` 16 | <page> 17 | <h1><title /></h1> 18 | <p>Windblade uses semantic colors.</p> 19 | 20 | <h2>Try it</h2> 21 | <try-it selected="$util"> 22 | <utils> 23 | <util> 24 | fill- 25 | <select> 26 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 27 | <option value="$name"/> 28 | </for> 29 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 30 | <option value="$name"/> 31 | </for> 32 | </select> 33 | </util> 34 | <util renderer="fg"> 35 | fill-fg- 36 | <input type="integer" /> 37 | </util> 38 | </utils> 39 | 40 | <renderer html="${encodeString(` 41 | TODO 42 | `)}" /> 43 | 44 | <h3>Preview</h3> 45 | <viewport /> 46 | 47 | <h3>HTML</h3> 48 | <html /> 49 | 50 | <h3>Generated CSS</h3> 51 | <css /> 52 | </try-it> 53 | </page> 54 | ` 55 | 56 | return { rules, docs } 57 | } 58 | 59 | export function stroke() { 60 | const rules: Rule<Theme>[] = [ 61 | color.colorRule('stroke', 'stroke'), 62 | color.fgColorRule('stroke-fg', 'stroke'), 63 | ] 64 | 65 | const docs: DocumentationPage = ` 66 | <page> 67 | <h1><title /></h1> 68 | <p>Windblade uses semantic colors.</p> 69 | 70 | <h2>Try it</h2> 71 | <try-it selected="$util"> 72 | <utils> 73 | <util> 74 | stroke- 75 | <select> 76 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 77 | <option value="$name"/> 78 | </for> 79 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 80 | <option value="$name"/> 81 | </for> 82 | </select> 83 | </util> 84 | <util renderer="fg"> 85 | stroke-fg- 86 | <input type="integer" /> 87 | </util> 88 | </utils> 89 | 90 | <renderer html="${encodeString(` 91 | TODO 92 | `)}" /> 93 | 94 | <h3>Preview</h3> 95 | <viewport /> 96 | 97 | <h3>HTML</h3> 98 | <html /> 99 | 100 | <h3>Generated CSS</h3> 101 | <css /> 102 | </try-it> 103 | </page> 104 | ` 105 | 106 | return { rules, docs } 107 | } 108 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/documented/typography.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { color } = ruleUtils 8 | 9 | export function textColor() { 10 | const rules: Rule<Theme>[] = [ 11 | color.colorRule('text', 'color'), 12 | color.fgColorRule('text-fg', 'color'), 13 | ] 14 | 15 | const docs: DocumentationPage = ` 16 | <page> 17 | <h1><title /></h1> 18 | <p>Windblade uses semantic colors.</p> 19 | 20 | <h2>Try it</h2> 21 | <try-it selected="$util"> 22 | <utils> 23 | <util> 24 | text- 25 | <select> 26 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 27 | <option value="$name"/> 28 | </for> 29 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 30 | <option value="$name"/> 31 | </for> 32 | </select> 33 | </util> 34 | <util renderer="fg"> 35 | text-fg- 36 | <input type="integer" /> 37 | </util> 38 | </utils> 39 | 40 | <renderer html="${encodeString(` 41 | <div class="$util">Lorem ipsum<div> 42 | `)}" /> 43 | 44 | <h3>Preview</h3> 45 | <viewport /> 46 | 47 | <h3>HTML</h3> 48 | <html /> 49 | 50 | <h3>Generated CSS</h3> 51 | <css /> 52 | </try-it> 53 | </page> 54 | ` 55 | 56 | return { rules, docs } 57 | } 58 | 59 | export function textDecorationColor() { 60 | const rules: Rule<Theme>[] = [ 61 | color.colorRule('decoration', 'text-decoration-color'), 62 | color.fgColorRule('decoration-fg', 'text-decoration-color'), 63 | ] 64 | 65 | const docs: DocumentationPage = ` 66 | <page> 67 | <h1><title /></h1> 68 | <p>Windblade uses semantic colors.</p> 69 | 70 | <h2>Try it</h2> 71 | <try-it selected="$util"> 72 | <utils> 73 | <util> 74 | decoration- 75 | <select> 76 | <for object="theme.windblade.colors" key-as="$name" value-as="$color"> 77 | <option value="$name"/> 78 | </for> 79 | <for object="theme.windblade.miscColors" key-as="$name" value-as="$color"> 80 | <option value="$name"/> 81 | </for> 82 | </select> 83 | </util> 84 | <util renderer="fg"> 85 | decoration-fg- 86 | <input type="integer" /> 87 | </util> 88 | </utils> 89 | 90 | <renderer html="${encodeString(` 91 | <div class="underline $util">Lorem ipsum<div> 92 | `)}" /> 93 | 94 | <h3>Preview</h3> 95 | <viewport /> 96 | 97 | <h3>HTML</h3> 98 | <html /> 99 | 100 | <h3>Generated CSS</h3> 101 | <css /> 102 | </try-it> 103 | </page> 104 | ` 105 | 106 | return { rules, docs } 107 | } 108 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/rules/index.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | 4 | import * as backgrounds from './documented/backgrounds' 5 | import * as borders from './documented/borders' 6 | import * as interactivity from './documented/interactivity' 7 | import * as svg from './documented/svg' 8 | import * as typography from './documented/typography' 9 | import * as accessibility from './documented/accessibility' 10 | 11 | const rules: Rule<Theme>[] = [ 12 | // Backgrounds 13 | ...backgrounds.bgColor().rules, 14 | ...backgrounds.backgroundImage().rules, 15 | ...backgrounds.gradientColorStops().rules, 16 | 17 | // Typography 18 | ...typography.textColor().rules, 19 | ...typography.textDecorationColor().rules, 20 | 21 | // Borders 22 | ...borders.borderColor().rules, 23 | ...borders.outlineColor().rules, 24 | 25 | // Effects 26 | 27 | // Filters 28 | 29 | // Tables 30 | 31 | // Transitions & Animations 32 | 33 | // Transforms 34 | 35 | // Interactivity 36 | ...interactivity.accentColor().rules, 37 | ...interactivity.caretColor().rules, 38 | 39 | // SVG 40 | ...svg.fill().rules, 41 | ...svg.stroke().rules, 42 | 43 | // Accessibility 44 | ...accessibility.colorScheme().rules, 45 | ] 46 | 47 | export default rules 48 | -------------------------------------------------------------------------------- /packages/unocss-preset-color/src/theme.ts: -------------------------------------------------------------------------------- 1 | import type { Theme } from '@windblade/core' 2 | 3 | const main: Theme = { 4 | windblade: { 5 | colors: { 6 | 'clear': { 7 | base: { dark: { l: 0, c: 0 } }, 8 | on: [ 9 | { dark: { l: 1, c: 0 } }, 10 | ], 11 | }, 12 | 13 | 'normal': { 14 | base: { dark: { l: 0, c: 0 } }, 15 | on: [ 16 | { dark: { l: 0.96, c: 0.01 } }, 17 | { dark: { l: 0.90, c: 0.02 } }, 18 | { dark: { l: 0.60, c: 0.04 } }, 19 | { dark: { l: 0.40, c: 0.02 } }, 20 | { dark: { l: 0.20, c: 0.01 } }, 21 | ], 22 | }, 23 | 24 | 'normal-2': { 25 | base: { dark: { l: 0.11, c: 0.001 }, light: { l: 0.98 } }, 26 | on: [ 27 | { dark: { l: 0.96, c: 0.01 } }, 28 | { dark: { l: 0.90, c: 0.02 } }, 29 | { dark: { l: 0.60, c: 0.04 } }, 30 | { dark: { l: 0.40, c: 0.02 } }, 31 | { dark: { l: 0.20, c: 0.01 } }, 32 | ], 33 | }, 34 | 35 | 'normal-3': { 36 | base: { dark: { l: 0.15, c: 0.004 }, light: { l: 0.94 } }, 37 | on: [ 38 | { dark: { l: 0.96, c: 0.01 } }, 39 | { dark: { l: 0.90, c: 0.04 }, light: { l: 0.3 } }, 40 | { dark: { l: 0.60, c: 0.06 }, light: { l: 0.4 } }, 41 | { dark: { l: 0.40, c: 0.02 }, light: { l: 0.8 } }, 42 | { dark: { l: 0.20, c: 0.01 }, light: { l: 0.9 } }, 43 | ], 44 | }, 45 | 46 | 'normal-4': { 47 | base: { dark: { l: 0.19, c: 0.006 }, light: { l: 0.9 } }, 48 | on: [ 49 | { dark: { l: 0.96, c: 0.01 } }, 50 | { dark: { l: 0.90, c: 0.04 }, light: { l: 0.3 } }, 51 | { dark: { l: 0.60, c: 0.06 }, light: { l: 0.4 } }, 52 | { dark: { l: 0.40, c: 0.02 }, light: { l: 0.8 } }, 53 | { dark: { l: 0.20, c: 0.01 }, light: { l: 0.9 } }, 54 | ], 55 | }, 56 | 57 | 'surface': { 58 | base: { dark: { l: 0.4, c: 0.04, a: 0.1 }, light: { c: 0.1 } }, 59 | on: [ 60 | { dark: { l: 0.9, c: 0.1 }, light: { l: 0.3 } }, 61 | { dark: { l: 0.8, c: 0.1 }, light: { l: 0.4 } }, 62 | { dark: { l: 0.6, c: 0.06 }, light: { l: 0.5 } }, 63 | { dark: { l: 0.4, c: 0.04 } }, 64 | { dark: { l: 0.2, c: 0.02 } }, 65 | ], 66 | }, 67 | 68 | 'accent': { 69 | base: { dark: { l: 0.7, c: 0.2 }, light: { l: 0.6 } }, 70 | on: [ 71 | { dark: { l: 0.1, c: 0.2 }, light: { l: 1 } }, 72 | { dark: { l: 0.2, c: 0.23 }, light: { l: 0.98 } }, 73 | { dark: { l: 0.4, c: 0.26 }, light: { l: 0.9 } }, 74 | { dark: { l: 0.6, c: 0.3 }, light: { l: 0.8 } }, 75 | ], 76 | }, 77 | 78 | 'accent-2': { 79 | base: { dark: { l: 0.7, c: 0.2, a: 0.4 }, light: { l: 0.6 } }, 80 | on: [ 81 | { dark: { l: 0.9, c: 0.1 }, light: { l: 0.2 } }, 82 | { dark: { l: 0.8, c: 0.15 }, light: { l: 0.3 } }, 83 | { dark: { l: 0.6, c: 0.2 }, light: { l: 0.5 } }, 84 | ], 85 | }, 86 | 87 | 'accent-3': { 88 | base: { dark: { l: 0.6, c: 0.06, a: 0.1 }, light: { c: 0.18 } }, 89 | on: [ 90 | { dark: { l: 0.9, c: 0.2 } }, 91 | ], 92 | }, 93 | 94 | 'accent-4': { 95 | base: { dark: { l: 0.4, c: 0.01, a: 0.1 } }, 96 | on: [ 97 | { dark: { l: 0.9, c: 0.2 } }, 98 | ], 99 | }, 100 | }, 101 | 102 | proportions: {}, 103 | 104 | time: { 105 | baseUnitMs: 150, 106 | 107 | functions: { 108 | default: 'linear', 109 | }, 110 | }, 111 | }, 112 | } 113 | 114 | export default main 115 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/unocss-preset-dollars", 3 | "version": "2.0.0-beta.7", 4 | "description": "", 5 | "exports": { 6 | ".": { 7 | "types": "./dist/index.d.ts", 8 | "require": "./dist/index.cjs", 9 | "import": "./dist/index.mjs" 10 | }, 11 | "./*": "./*" 12 | }, 13 | "main": "dist/index.cjs", 14 | "module": "dist/index.mjs", 15 | "types": "dist/index.d.ts", 16 | "files": [ 17 | "dist", 18 | "*.css" 19 | ], 20 | "scripts": { 21 | "build": "unbuild", 22 | "stub": "unbuild --stub" 23 | }, 24 | "dependencies": { 25 | "@unocss/core": "0.55.0", 26 | "@windblade/core": "workspace:*", 27 | "@windblade/unocss-docs": "workspace:*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/docs/dollarSyntax.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example1 = ` 5 | <div class="grid grid-cols-[auto_$l_auto]"></div> 6 | ` 7 | 8 | const example2 = ` 9 | <div class="p-s"> 10 | Label 11 | <!-- Custom underline --> 12 | <div class="absolute size-i-full size-b-s.2 inset-bottom-$(($s-$s.2)/2)"></div> 13 | </div> 14 | ` 15 | 16 | const colors: DocumentationPage = ` 17 | <page> 18 | <h1>Using the $ syntax</h1> 19 | <p>You can grab proportions from your theme by prefixing their names with <code>$</code>. This is mainly useful inside brackets when you want to set custom values.</p> 20 | <pre lang="html" code="${encodeString(example1)}" /> 21 | 22 | <p>You can also use <code>$</code> to conveniently perform operations with your design tokens without breaking out of your design system or using <code>calc()</code>.</p> 23 | <pre lang="html" code="${encodeString(example2)}" /> 24 | 25 | <p>Note that <code>$</code> is an UnoCSS <a href="https://github.com/unocss/unocss#custom-variants">variant</a> so it works with UnoCSS utilities that come from other presets too!</p> 26 | </page> 27 | ` 28 | 29 | export default colors 30 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/docs/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import installation from './installation' 3 | import options from './options' 4 | import dollarSyntax from './dollarSyntax' 5 | 6 | const main: DocumentationTree = new Map([ 7 | ['Usage', new Map([ 8 | ['Installation', installation], 9 | ['Options', options], 10 | ['$ syntax', dollarSyntax], 11 | ])], 12 | ]) 13 | 14 | export default main 15 | export * as dollarSyntax from './dollarSyntax' 16 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/docs/installation.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example = ` 5 | import { defineConfig } from 'unocss'; 6 | import windbladeDollars from '@windblade/unocss-preset-dollars'; 7 | 8 | export default defineConfig({ 9 | presets: [ 10 | windbladeDollars(), 11 | ], 12 | });` 13 | 14 | const main: DocumentationPage = ` 15 | <page> 16 | <h1><title /></h1> 17 | <p>Just like the complete Windblade, the Dollars module is an UnoCSS preset, please follow its own <a href="https://unocss.dev/integrations">guide</a> to install it.</p> 18 | <p>Once UnoCSS is installed in your project simply get @windblade/unocss-preset-dollars from npm and add it to the presets array.</p> 19 | <pre lang="sh" code="npm install @windblade/unocss-preset-dollars" /> 20 | <pre lang="ts" code="${encodeString(example)}" /> 21 | <p>That's it! You can now use Windblade's dollar syntax in your project.</p> 22 | </page> 23 | ` 24 | 25 | export default main 26 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/docs/options.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example = ` 5 | import { defineConfig } from 'vite' 6 | import unocss from '@unocss/vite' 7 | import presetDollars from '@windblade/unocss-preset-dollars' 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | unocss({ 12 | presets: [ 13 | presetDollars({ 14 | getVariables: () => ([ 15 | ['small', 0.5], 16 | ['normal', 1], 17 | ['large', 2], 18 | ]), 19 | }), 20 | ], 21 | }), 22 | ], 23 | }) 24 | ` 25 | 26 | const example2 = ` 27 | import { defineConfig } from 'vite' 28 | import unocss from '@unocss/vite' 29 | import presetDollars from '@windblade/unocss-preset-dollars' 30 | 31 | export default defineConfig({ 32 | plugins: [ 33 | unocss({ 34 | theme: { 35 | things: { 36 | a: 'foo', 37 | b: 'bar', 38 | color: 'oklch(80 0.2 0.8)', 39 | size: '2rem', 40 | unit: 16, 41 | } 42 | } 43 | presets: [ 44 | presetDollars({ 45 | getVariables: (theme) => Object.entries(theme.things), 46 | }), 47 | ], 48 | }), 49 | ], 50 | }) 51 | ` 52 | 53 | const main: DocumentationPage = ` 54 | <page> 55 | <h1><title /></h1> 56 | <p>You can customize where the dollars (key-value pairs) are coming from.</p> 57 | 58 | <p>Define a <code>getVariables</code> function inside the preset options that returns custom dollars as an array of key-value tuples.</p> 59 | <pre lang="ts" code="${encodeString(example)}" /> 60 | 61 | <p>You can also use the <code>theme</code> argument passed to the <code>getVariables</code> to repalce keys with values from your theme.</p> 62 | <p>Make sure that values have a <code>toString</code> metod.</p> 63 | <pre lang="ts" code="${encodeString(example2)}" /> 64 | 65 | <p>If you don't provide this option the preset will make key-value pairs out of the <code>theme.windblade.proportions</code> object.</p> 66 | </page> 67 | ` 68 | 69 | export default main 70 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Preset, PresetOptions } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | import { createVariants } from './variants' 4 | 5 | export interface Stringifiable { 6 | toString: () => string 7 | } 8 | export type Dollar = [string, Stringifiable] 9 | export type DollarGetter = (theme: Theme) => Dollar[] 10 | 11 | export interface Options extends PresetOptions { 12 | /** 13 | * Getter of a 'name + value' tuple array that defines variable names and values that they represent. 14 | * These tuples are used at build time to replace `$<name>` with `$<value>` inside all utils. 15 | * Even if they are not from Windblade. 16 | * 17 | * @param theme 18 | * @returns array of name + value tuples. 19 | */ 20 | getVariables?: DollarGetter 21 | } 22 | 23 | function main(options: Options = {}): Preset<Theme> { 24 | const dolarGetter: DollarGetter 25 | = options?.getVariables 26 | ?? (theme => theme.windblade?.proportions ? Object.entries(theme.windblade.proportions) : []) 27 | 28 | return { 29 | name: '@windblade/unocss-preset-dollars', 30 | options, 31 | variants: createVariants(dolarGetter), 32 | prefix: undefined, 33 | } 34 | } 35 | 36 | export default main 37 | export * as docs from './docs' 38 | export * from './variants' 39 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/variants/dollars.ts: -------------------------------------------------------------------------------- 1 | import type { Variant } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | import type { DollarGetter } from '..' 4 | 5 | export function resolveDollars(expr: string, theme: Theme, getter: DollarGetter): string { 6 | let resolved = expr 7 | 8 | // Resolve variables 9 | getter(theme).forEach(([name, value]) => { 10 | resolved = resolved.replaceAll(`$${name}`, value.toString()) 11 | }) 12 | 13 | // Resolve expressions 14 | while (resolved.includes('$(')) { 15 | const start = resolved.indexOf('$') + 1 16 | const rest = resolved.substring(start) 17 | 18 | // Isolate expression 19 | const parenStart = 0 20 | let parenEnd = parenStart 21 | let open = 0 22 | for (let i = 0; i < rest.length; ++i) { 23 | if (rest[i] === '(') 24 | ++open 25 | if (rest[i] === ')') 26 | --open 27 | 28 | if (open === 0) { 29 | parenEnd = i + 1 30 | break 31 | } 32 | } 33 | const parenExpr = rest.substring(parenStart, parenEnd) 34 | 35 | // Evaluate and resolve 36 | try { 37 | // eslint-disable-next-line no-new-func 38 | resolved = resolved.replace(`$${parenExpr}`, Function(`'use strict'; return (${parenExpr})`)()) 39 | } 40 | catch (_) { 41 | // bail if someting goes wrong and return original expression completely unresolved 42 | return expr 43 | } 44 | } 45 | 46 | return resolved 47 | } 48 | 49 | function createVariant(getter: DollarGetter): Variant<Theme> { 50 | return (matcher, { theme }) => { 51 | return { 52 | matcher: resolveDollars(matcher, theme, getter), 53 | } 54 | } 55 | } 56 | 57 | export default createVariant 58 | -------------------------------------------------------------------------------- /packages/unocss-preset-dollars/src/variants/index.ts: -------------------------------------------------------------------------------- 1 | import type { Variant } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | import type { DollarGetter } from '..' 4 | import createVariantDollars from './dollars' 5 | 6 | export function createVariants(dolarGetter: DollarGetter): Variant<Theme>[] { 7 | return [ 8 | createVariantDollars(dolarGetter), 9 | ] 10 | } 11 | 12 | export { createVariantDollars } 13 | -------------------------------------------------------------------------------- /packages/unocss-preset/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | -------------------------------------------------------------------------------- /packages/unocss-preset/README.md: -------------------------------------------------------------------------------- 1 | <h1 align="center"> 2 | <a href="https://starlederer.github.io/windblade" target="_blank"> 3 | <img alt="Windblade" src="https://raw.githubusercontent.com/starlederer/windblade/HEAD/packages/brand/logo.svg" width="64" height="64" style="max-inline-size: 100%;"> 4 | </a> 5 | 6 | Windblade 7 | </h1> 8 | 9 | <p align="center"> 10 | Tailwind-inspired UnoCSS preset with a better color system, logical properties, and simpler customization. 11 | </p> 12 | 13 | <p align="center"> 14 | <a href="https://starlederer.github.io/windblade">Homepage</a> | <a href="https://starlederer.github.io/windblade?navigation=/docs">Documentation</a> (WIP) | <a href="https://unocss.dev">UnoCSS</a> | <a href="https://tailwindcss.com/">Tailwind</a> 15 | </p> 16 | 17 | ## Installation 18 | 19 | Please see the [Docs](https://starlederer.github.io/windblade?navigation=/docs/Usage/Installation) for installation instrucions. 20 | 21 | ## Usage 22 | 23 | Documentation is in development. Until it is complete please follow [Tailwind's docs](https://tailwindcss.com/docs/aspect-ratio) and refer to our [documentation](https://starlederer.github.io/windblade?navigation=/docs) for differences. *You will not have to refer to Tailwind's docs once our docuentation is complete.* 24 | 25 | ## Sponsors 26 | 27 | <p align="center"> 28 | <a href="https://starlederer.github.io/sponsors/sponsors.svg"> 29 | <img src="https://starlederer.github.io/sponsors/sponsors.svg"/> 30 | </a> 31 | </p> 32 | -------------------------------------------------------------------------------- /packages/unocss-preset/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild' 2 | 3 | export default defineBuildConfig({ 4 | entries: [ 5 | 'src/index', 6 | ], 7 | clean: true, 8 | declaration: true, 9 | externals: [ 10 | 'unconfig', 11 | 'magic-string', 12 | '@unocss/core', 13 | '@unocss/config', 14 | ], 15 | rollup: { 16 | emitCJS: true, 17 | dts: { 18 | respectExternal: false, 19 | }, 20 | }, 21 | }) 22 | -------------------------------------------------------------------------------- /packages/unocss-preset/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/unocss-preset", 3 | "version": "2.0.0-beta.7", 4 | "description": "Tailwind-inspired UnoCSS preset with considerable improvements", 5 | "author": "Star Lederer <germans.lederers@gmail.com>", 6 | "license": "MIT", 7 | "funding": "https://github.com/sponsors/StarLederer", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/StarLederer/windblade.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/StarLederer/windblade/issues" 14 | }, 15 | "keywords": [ 16 | "unocss", 17 | "unocss-preset", 18 | "tailwind", 19 | "windblade" 20 | ], 21 | "sideEffects": false, 22 | "exports": { 23 | ".": { 24 | "types": "./dist/index.d.ts", 25 | "require": "./dist/index.cjs", 26 | "import": "./dist/index.mjs" 27 | }, 28 | "./*": "./*" 29 | }, 30 | "main": "dist/index.cjs", 31 | "module": "dist/index.mjs", 32 | "types": "dist/index.d.ts", 33 | "files": [ 34 | "dist", 35 | "*.css" 36 | ], 37 | "scripts": { 38 | "build": "unbuild", 39 | "stub": "unbuild --stub" 40 | }, 41 | "dependencies": { 42 | "@unocss/core": "0.55.0", 43 | "@unocss/preset-mini": "0.55.0", 44 | "@windblade/core": "workspace:*", 45 | "@windblade/unocss-docs": "workspace:*", 46 | "@windblade/unocss-preset-color": "workspace:*", 47 | "@windblade/unocss-preset-dollars": "workspace:*", 48 | "ts-deepmerge": "^6.1.0", 49 | "ts-extras": "^0.11.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import usage from './usage' 3 | import theme from './theme' 4 | import rules from './rules' 5 | 6 | const main: DocumentationTree = new Map([ 7 | ['Usage', usage], 8 | ['Theme', theme], 9 | ...rules, 10 | ]) 11 | 12 | export default main 13 | export * as usage from './usage' 14 | export * as theme from './theme' 15 | export * as rules from './rules' 16 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/rules/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import { docs } from '@windblade/unocss-preset-color' 3 | 4 | import * as layout from '../../rules/documented/layout' 5 | import * as flexboxAndGrid from '../../rules/documented/flexboxAndGrid' 6 | import * as spacing from '../../rules/documented/spacing' 7 | import * as sizing from '../../rules/documented/sizing' 8 | import * as backgrounds from '../../rules/documented/backgrounds' 9 | import * as typography from '../../rules/documented/typography' 10 | import * as borders from '../../rules/documented/borders' 11 | import * as effects from '../../rules/documented/effects' 12 | import * as filters from '../../rules/documented/filters' 13 | import * as tables from '../../rules/documented/tables' 14 | import * as transitionsAndAnimation from '../../rules/documented/transitionsAndAnimation' 15 | import * as transforms from '../../rules/documented/transforms' 16 | import * as interactivity from '../../rules/documented/interactivity' 17 | import * as svg from '../../rules/documented/svg' 18 | 19 | const main: DocumentationTree = new Map([ 20 | ['Layout', new Map([ 21 | ['Aspect Ratio', layout.aspectRatio().docs], 22 | ['Container', layout.container().docs], 23 | ['Break After', layout.breakAfter().docs], 24 | ['Break Before', layout.breakBefore().docs], 25 | ['Disaply', layout.display().docs], 26 | ['Object Position', layout.objectPosition().docs], 27 | ])], 28 | ['Flexbox & Grid', new Map([ 29 | ['Grid Auto Columns', flexboxAndGrid.gridAutoCols().docs], 30 | ['Grid Auto Rows', flexboxAndGrid.gridAutoRows().docs], 31 | ['Grid Fit Columns', flexboxAndGrid.gridFitCols().docs], 32 | ['Grid Fit Rows', flexboxAndGrid.gridFitRows().docs], 33 | ['Grid Fill Clumns', flexboxAndGrid.gridFillCols().docs], 34 | ['Grid Fill Rows', flexboxAndGrid.gridFillRows().docs], 35 | ])], 36 | ['Spacing', new Map([ 37 | ['Padding', spacing.padding().docs], 38 | ['Margin', spacing.margin().docs], 39 | ['Space between', spacing.spaceBetween().docs], 40 | ])], 41 | ['Sizing', new Map([ 42 | ['Width & Height', sizing.widthHeight().docs], 43 | ['Size', sizing.size().docs], 44 | ['Min-Size', sizing.minSize().docs], 45 | ['Max-Size', sizing.maxSize().docs], 46 | ])], 47 | ['Background', new Map([ 48 | ['Color', docs.rules.backgrounds.bgColor().docs], 49 | ['Background Position', backgrounds.backgroundPosition().docs], 50 | ])], 51 | ['Typography', new Map([ 52 | ['Font Family', typography.fontFamily().docs], 53 | ['Font Size', typography.fontSize().docs], 54 | ['Font Smoothing', typography.fontSmoothing().docs], 55 | ['Letter Spacing', typography.tracking().docs], 56 | ['Line Height', typography.leading().docs], 57 | ['Text Color', docs.rules.typography.textColor().docs], 58 | ['Text Decoration Color', docs.rules.typography.textDecorationColor().docs], 59 | ['Text Decoration Thickness', typography.textDecorationThickness().docs], 60 | ['Text Underline Offset', typography.textUnderlineOffset().docs], 61 | ])], 62 | ['Borders', new Map([ 63 | ['Border Radius', borders.borderRadius().docs], 64 | ['Border Width', borders.borderWidth().docs], 65 | ['Border Color', docs.rules.borders.borderColor().docs], 66 | ['Outline Width', borders.outlineWidth().docs], 67 | ['Outline Offset', borders.outlineOffset().docs], 68 | ['Outline Color', docs.rules.borders.outlineColor().docs], 69 | ['Divide', borders.divide().docs], 70 | ['Ring', borders.ring().docs], 71 | ])], 72 | ['Effects', new Map([ 73 | ['Box Shadow', effects.boxShadow().docs], 74 | ['Opacity', effects.opacity().docs], 75 | ])], 76 | ['Filters', new Map([ 77 | ['Drop Shadow', filters.dropShadow().docs], 78 | ])], 79 | ['Tables', new Map([ 80 | ['Border spacing', tables.borderSpacing().docs], 81 | ])], 82 | ['Transitions & Animation', new Map([ 83 | ['Transition Delay & Duration', transitionsAndAnimation.transitionDelayAndDuration().docs], 84 | ['Transition Timing Function', transitionsAndAnimation.transitionTimingFunction().docs], 85 | ['Animations', transitionsAndAnimation.animation().docs], 86 | ['Animation Delay & Duration', transitionsAndAnimation.animationDelayAndDuration().docs], 87 | ['Animation Timing Function', transitionsAndAnimation.animationTimingFunction().docs], 88 | ])], 89 | ['Transforms', new Map([ 90 | ['Scale', transforms.scale().docs], 91 | ['Rotate', transforms.rotate().docs], 92 | ['Translate', transforms.translate().docs], 93 | ['Skew', transforms.skew().docs], 94 | ])], 95 | ['Interactivity', new Map([ 96 | ['Scroll margin', interactivity.scrollMargin().docs], 97 | ['Scroll padding', interactivity.scrollPadding().docs], 98 | ['Scroll snap type', interactivity.scrollSnapType().docs], 99 | ['Touch action', interactivity.touchAction().docs], 100 | ])], 101 | ['SVG', new Map([ 102 | ['Fill', docs.rules.svg.fill().docs], 103 | ['Stroke Color', docs.rules.svg.stroke().docs], 104 | ['Stroke Width', svg.strokeWidth().docs], 105 | ])], 106 | ['Accessibility', new Map([ 107 | ['Color Scheme', docs.rules.accessibility.colorScheme().docs], 108 | ])], 109 | ]) 110 | 111 | export default main 112 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/theme/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import { docs as colorDocs } from '@windblade/unocss-preset-color' 3 | import proportions from './proportions' 4 | import other from './other' 5 | 6 | const colors = colorDocs.usage.theme 7 | 8 | export const categoy: DocumentationTree = new Map([ 9 | ['Semantic Colors', colors], 10 | ['Proportions', proportions], 11 | ['Other', other], 12 | ]) 13 | 14 | export { 15 | colors, 16 | } 17 | 18 | export default categoy 19 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/theme/other.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example = ` 5 | type ThemeColor = { 6 | dark: { 7 | l: number; 8 | c: number; 9 | a?: number; 10 | }; 11 | light?: { 12 | l?: number; 13 | c?: number; 14 | a?: number; 15 | }; 16 | }; 17 | 18 | type ThemeColorCombo = { 19 | base: ThemeColor; 20 | on: ThemeColor[]; 21 | }; 22 | 23 | type Theme = { 24 | windblade: { 25 | colors: Record<string, ThemeColorCombo>; 26 | miscColors?: Record<string, string>; 27 | proportions: Record<string, number>; 28 | miscSizes?: Record<string, string>; 29 | time: { 30 | baseUnitMs: number; 31 | functions: Record<string, string> & { 32 | default: string; 33 | }; 34 | }; 35 | }; 36 | }; 37 | ` 38 | 39 | const main: DocumentationPage = ` 40 | <page> 41 | <h1>Other theme objects</h1> 42 | <p>See the theme type below for other values you can customize.</p> 43 | 44 | <h2>Theme type</h2> 45 | <pre lang="ts" code="${encodeString(example)}" /> 46 | </page> 47 | ` 48 | 49 | export default main 50 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/theme/proportions.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | import { themes } from '@windblade/core' 4 | 5 | const wb = themes.windblade.windblade 6 | 7 | const styles = { 8 | tr: 'border border-0 border-b-px border-color-surface', 9 | th: 'p-b-s.4 p-ie-s font-bold text-fg-2', 10 | td: 'p-b-s.4 p-ie-s text-fg-3', 11 | } 12 | 13 | const example = ` 14 | const theme: Theme = { 15 | windblade: { 16 | proportions: { 17 | 'half': 0.5, 18 | 'full': 1, 19 | 'double': 2, 20 | }, 21 | }, 22 | };` 23 | 24 | const main: DocumentationPage = ` 25 | <page> 26 | <h1><title /></h1> 27 | <p>Proportions are used throughout the whole preset for size, duration, opacity, etc.</p> 28 | <p>Since windblade uses the same proportions for everything it is very easy to customize and is highly recommended that you change them to match your design system.</p> 29 | 30 | <h2>Default proportions</h2> 31 | <p>By default Windblade is configured with 10-unit-based proportions, however, Windblade also includes an option to use Tailwind and Material Design v3 proportions.</p> 32 | <example html="${encodeString(` 33 | <table class="border-collapse"> 34 | <tr class="${styles.tr}"> 35 | <th class="${styles.th}">Name</th> 36 | <th class="${styles.th}">Value</th> 37 | <th class="${styles.th}"></th> 38 | </tr> 39 | ${((): string => { 40 | const proportions = wb?.proportions 41 | if (typeof proportions === 'object') { 42 | return Object.entries(proportions as Record<string, number>).map(([name, value]) => ` 43 | <tr class="${styles.tr}"> 44 | <td class="${styles.td} font-semibold">${name}</td> 45 | <td class="${styles.td} text-fg-4">${value}</td> 46 | <td class="${styles.td}"><div class="bg-accent rounded-s.2 size-b-s size-i-${name}"></div></td> 47 | </tr> 48 | `).join('') 49 | } 50 | return '' 51 | })()} 52 | </table> 53 | `)}" /> 54 | 55 | <h2>Custom proportions</h2> 56 | <p>Add custom proportions by adding numbers like the following:</p> 57 | <pre lang="ts" code="${encodeString(example)}" /> 58 | 59 | <p>Proportions are converted to relevant units automatically. Rem is used for sizing, ms for timing, raw number for oapcity, etc.</p> 60 | </page> 61 | ` 62 | 63 | export default main 64 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/usage/index.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import { docs as dollarsDocs } from '@windblade/unocss-preset-dollars' 3 | import { docs as colorDocs } from '@windblade/unocss-preset-color' 4 | import installation from './installation' 5 | import logicalProperties from './logicalProperties' 6 | import options from './options' 7 | import variants from './variants' 8 | 9 | const dollarSyntax = dollarsDocs.dollarSyntax.default 10 | const semanticColors = colorDocs.usage.semanticColors 11 | 12 | export const categoy: DocumentationTree = new Map([ 13 | ['Installation', installation], 14 | ['Options', options], 15 | ['Semantic colors', semanticColors], 16 | ['Logical properties', logicalProperties], 17 | ['$ syntax', dollarSyntax], 18 | ['Hover, focus and other states', variants], 19 | ]) 20 | 21 | export { 22 | installation, 23 | semanticColors, 24 | logicalProperties, 25 | dollarSyntax, 26 | variants, 27 | options, 28 | } 29 | 30 | export default categoy 31 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/usage/installation.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const example = ` 5 | import { defineConfig } from 'unocss'; 6 | import presetWindblade from '@windblade/unocss-preset'; 7 | 8 | export default defineConfig({ 9 | presets: [ 10 | presetWindblade(), 11 | ], 12 | });` 13 | 14 | const addition1 = `import { defineConfig } from 'unocss'; 15 | import presetWindblade from '@windblade/unocss-preset'; 16 | import presetVariants from 'unocss-preset-mini-variants'; 17 | 18 | export default defineConfig({ 19 | presets: [ 20 | presetWindblade(), 21 | presetVariants(), 22 | ], 23 | });` 24 | 25 | const addition2 = `import { defineConfig } from 'unocss'; 26 | import presetWindblade from '@windblade/unocss-preset'; 27 | import transformerDirectives from '@unocss/transformer-directives'; 28 | 29 | export default defineConfig({ 30 | presets: [ 31 | presetWindblade(), 32 | ], 33 | transformers: [ 34 | transformerDirectives(), 35 | ], 36 | });` 37 | 38 | const main: DocumentationPage = ` 39 | <page> 40 | <h1><title /></h1> 41 | <p>Windblade is an UnoCSS preset, please follow its own <a href="https://unocss.dev/integrations">guide</a> to install it.</p> 42 | <p>Once UnoCSS is installed in your project simply get Windblade from npm and add it to the presets array.</p> 43 | <pre lang="sh" code="npm install @windblade/unocss-preset" /> 44 | <pre lang="ts" code="${encodeString(example)}" /> 45 | 46 | <h2>Recommended additions</h2> 47 | <p>Windblade can be used by itself, however there are other UnoCSS presets that we recommend using together with it.</p> 48 | 49 | <h3>Getting hover, active, etc.</h3> 50 | <p>Windblade does not come with combinators, pseudo-selectors or other query modifiers so you need to get this functionality elsewhere. We recommend using unocss-preset-mini-variants.</p> 51 | <pre lang="sh" code="npm install unocss-preset-mini-variants" /> 52 | <pre lang="ts" code="${encodeString(addition1)}" /> 53 | 54 | <h3>Getting @apply</h3> 55 | <p>UnoCSS offers an official solution for getting @apply in your projects. We recommend to use that if you need this functionality.</p> 56 | <pre lang="sh" code="npm install -D @unocss/transformer-directives" /> 57 | <pre lang="ts" code="${encodeString(addition2)}" /> 58 | </page> 59 | ` 60 | 61 | export default main 62 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/usage/logicalProperties.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | 4 | const styles = { 5 | block: 'bg-surface p-s transition text-center rounded-s.4', 6 | } 7 | 8 | const example = ` 9 | <div class="grid grid-cols-3 grid-auto-rows-m.2 gap-s.2 rounded-s overflow-hidden"> 10 | <div class="${styles.block}">ss</div> 11 | <div class="${styles.block}">bs</div> 12 | <div class="${styles.block}">se</div> 13 | 14 | <div class="${styles.block}">is</div> 15 | <div class="${styles.block}">center</div> 16 | <div class="${styles.block}">ie</div> 17 | 18 | <div class="${styles.block}">es</div> 19 | <div class="${styles.block}">be</div> 20 | <div class="${styles.block}">ee</div> 21 | </div>` 22 | 23 | const main: DocumentationPage = ` 24 | <page> 25 | <h1>Using logical properties</h1> 26 | <p>Windblade uses logical properties and values only.</p> 27 | 28 | <p>All properties that can be customized on multiple axis/edges/corenrs can be prepended with:</p> 29 | <ul> 30 | <li><code>-b</code> for block axis (e.g. <code>size-b</code>).</li> 31 | <li><code>-i</code> for inline axis (e.g. <code>size-i</code>).</li> 32 | <li><code>-bs</code> and <code>-be</code> for block start and end edges.</li> 33 | <li><code>-is</code> and <code>-ie</code> for inline start and end edges.</li> 34 | <li><code>-ss</code> <code>-se</code> <code>-es</code> <code>-ee</code> for corners (start start, start end, end start & end end).</li> 35 | </ul> 36 | 37 | <example html="${encodeString(example)}" /> 38 | 39 | <p>Windblade polyfills logical values so you can use this even where CSS does not support it yet (e.g. <code>background-position</code> with <code>bg-{corner}</code> utility).</p> 40 | <p>If you are new to logical properties try playing with <code>bg-gradient-to-{edge/corner}</code> and see which way the gradient goes.</p> 41 | <p>Please note that <code>width</code> and <code>height</code> are completely removed in favor of <code>size-{axis}</code>.</p> 42 | </page> 43 | ` 44 | 45 | export default main 46 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/usage/options.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | import { encodeString } from '@windblade/unocss-docs' 3 | import { themes } from '@windblade/core' 4 | 5 | const theme = ` 6 | import { defineConfig } from 'unocss'; 7 | import presetWindblade from '@windblade/unocss-preset'; 8 | 9 | export default defineConfig({ 10 | presets: [ 11 | presetWindblade({ 12 | theme: 'windblade', 13 | }), 14 | ], 15 | });` 16 | 17 | const main: DocumentationPage = ` 18 | <page> 19 | <h1><title /></h1> 20 | <p>At the moment Windblade only exposes one option which configures which theme preset is used.</p> 21 | <p>To specify which preset to use define the <code>theme</code> value in the preset options:</p> 22 | <pre lang="ts" code="${encodeString(theme)}" /> 23 | 24 | <p>The following themes are available:</p> 25 | <ul> 26 | ${Object.keys(themes).map(name => `<li><code>${name}</code></li>`).join('')} 27 | </ul> 28 | 29 | <p><small>Please note that the <code>material3</code> theme is not finished and is almost unusable at the moment.</small></p> 30 | </page> 31 | ` 32 | 33 | export default main 34 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/docs/usage/variants.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | 3 | const main: DocumentationPage = ` 4 | <page> 5 | <h1>Hover, focus and other states</h1> 6 | <p>Windblade does not come with functionality like hover or focus states. Please use Windblade together with <a href="https://www.npmjs.com/package/unocss-preset-mini-variants">unocss-preset-mini-variants</a> or other variant implementations if you need this functionality.</p> 7 | </page> 8 | ` 9 | 10 | export default main 11 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Preset, PresetOptions } from '@unocss/core' 2 | import type { Theme, WindbladeTheme } from '@windblade/core' 3 | import { theme, themes } from '@windblade/core' 4 | import merge from 'ts-deepmerge' 5 | import presetColor, { theme as colorTheme } from '@windblade/unocss-preset-color' 6 | import presetDollars from '@windblade/unocss-preset-dollars' 7 | import rules from './rules' 8 | import preflights from './preflights' 9 | 10 | export interface WindbladeOptions extends PresetOptions { 11 | theme?: WindbladeTheme 12 | } 13 | 14 | function main(options: WindbladeOptions = {}): Preset<Theme> { 15 | options.theme = options.theme ?? 'windblade' 16 | 17 | // Create presets that we inherit 18 | const pColor = presetColor() 19 | const pDollars = presetDollars() 20 | 21 | return { 22 | name: '@windblade/unocss-preset', 23 | theme: merge( 24 | theme, 25 | colorTheme, 26 | themes[options.theme], 27 | ) as Theme, 28 | rules: [ 29 | ...pColor.rules ?? [], 30 | ...pDollars.rules ?? [], 31 | ...rules, 32 | ], 33 | variants: [ 34 | ...pColor.variants ?? [], 35 | ...pDollars.variants ?? [], 36 | ], 37 | options: { 38 | ...pColor.options, 39 | ...pDollars.options, 40 | ...options, 41 | }, 42 | postprocess: [], 43 | preflights: [ 44 | ...pColor.preflights ?? [], 45 | ...pDollars.preflights ?? [], 46 | ...preflights, 47 | ], 48 | prefix: undefined, 49 | shortcuts: [], 50 | } 51 | } 52 | 53 | export default main 54 | export * as docs from './docs' 55 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/preflights/index.ts: -------------------------------------------------------------------------------- 1 | import type { Preflight } from '@unocss/core' 2 | import type { Theme } from '@windblade/core' 3 | 4 | const preflights: Preflight<Theme>[] = [ 5 | // Setup 6 | { 7 | getCSS: () => ` 8 | * { 9 | padding: 0; 10 | margin: 0; 11 | 12 | color: inherit; 13 | background: none; 14 | border: none; 15 | font: inherit; 16 | line-height: 1; 17 | 18 | box-sizing: border-box; 19 | } 20 | 21 | :root { 22 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 23 | font-size: 16px; 24 | font-weight: normal; 25 | 26 | font-synthesis: none; 27 | text-rendering: optimizeLegibility; 28 | -webkit-font-smoothing: antialiased; 29 | -moz-osx-font-smoothing: grayscale; 30 | -webkit-text-size-adjust: 100%; 31 | } 32 | `, 33 | }, 34 | 35 | // Polyfill logical values 36 | // TODO: add writimg mode queries when CSS implements it 37 | // TODO: remove when CSS implements logical values 38 | { 39 | getCSS: () => ` 40 | :root { 41 | --block-start: top; 42 | --block-end: bottom; 43 | --inline-start: left; 44 | --inline-end: right; 45 | --start-start: top left; 46 | --start-end: top right; 47 | --end-start: bottom left; 48 | --end-end: bottom right; 49 | } 50 | `, 51 | }, 52 | ] 53 | 54 | export default preflights 55 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/backgrounds.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { objectEntries } from 'ts-extras' 5 | import { ruleUtils } from '@windblade/core' 6 | import type { Theme } from '@windblade/core' 7 | 8 | const { logical } = ruleUtils 9 | 10 | export function backgroundPosition() { 11 | const rules: Rule<Theme>[] = [ 12 | ...objectEntries(logical.abbreviations.edges).map(([key, val]): Rule<Theme> => [ 13 | `bg-${key}`, { 'background-position': `var(--${val})` }, 14 | ]), 15 | ...objectEntries(logical.abbreviations.coners).map(([key, val]): Rule<Theme> => [ 16 | `bg-${key}`, { 'background-position': `var(--${val})` }, 17 | ]), 18 | ['bg-center', { 'background-position': 'center' }], 19 | ] 20 | 21 | const docs: DocumentationPage = ` 22 | <page> 23 | <h1><title /></h1> 24 | <p>Physical properties replaced with logocal.</p> 25 | 26 | <h2>Try it</h2> 27 | <try-it selected="$util"> 28 | <utils>${[ 29 | ...Object.keys(logical.abbreviations.edges), 30 | ...Object.keys(logical.abbreviations.coners), 31 | 'center', 32 | ].map(val => ` 33 | <util>bg-${val}</util> 34 | `).join('')}</utils> 35 | 36 | <renderer html="${encodeString(` 37 | <div class="$util rounded-s.4 size-i-full max-size-i-l.2 aspect-10/6" style="background-image: url('https://picsum.photos/600/400')"></div> 38 | `)}" /> 39 | 40 | <h3>Preview</h3> 41 | <viewport /> 42 | 43 | <h3>HTML</h3> 44 | <html /> 45 | 46 | <h3>Generated CSS</h3> 47 | <css /> 48 | </try-it> 49 | </page> 50 | ` 51 | 52 | return { rules, docs } 53 | } 54 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/doc-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './iterMultiple' 2 | export * from './logical' 3 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/doc-components/iterMultiple.ts: -------------------------------------------------------------------------------- 1 | export function iterObjects(unoObjects: string[], keyAs: string, valueAs: string, innerXml: string) { 2 | return unoObjects.map(obj => ` 3 | <for object="${obj}" key-as="${keyAs}" value-as="${valueAs}"> 4 | ${innerXml} 5 | </for> 6 | `).join('') 7 | } 8 | 9 | export function iterArrays(unoArrays: string[], valueAs: string, innerXml: string) { 10 | return unoArrays.map(obj => ` 11 | <for array="${obj}" value-as="${valueAs}"> 12 | ${innerXml} 13 | </for> 14 | `).join('') 15 | } 16 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/doc-components/logical.ts: -------------------------------------------------------------------------------- 1 | import { ruleUtils } from '@windblade/core' 2 | 3 | const { logical } = ruleUtils 4 | 5 | export function selectLogical(options: { 6 | axis?: boolean 7 | edges?: boolean 8 | corners?: boolean 9 | }) { 10 | return ` 11 | <select> 12 | ${[ 13 | ...(options.axis ? Object.keys(logical.abbreviations.axis) : []), 14 | ...(options.edges ? Object.keys(logical.abbreviations.edges) : []), 15 | ...(options.corners ? Object.keys(logical.abbreviations.coners) : []), 16 | ].map(pos => ` 17 | <option value="${pos}"/> 18 | `).join('')} 19 | </select> 20 | ` 21 | } 22 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/effects.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { size } = ruleUtils 8 | 9 | export function boxShadow() { 10 | const docs: DocumentationPage = ` 11 | <page> 12 | <h1><title /></h1> 13 | <p>Box shadows are removed for now because Tailwind's implementation is too limiting. Discussion in progress.</p> 14 | </page> 15 | ` 16 | 17 | return { rules: [], docs } 18 | } 19 | 20 | export function opacity() { 21 | const rules: Rule<Theme>[] = [size.rule('opacity', 'opacity', { defaultUnit: '' })] 22 | 23 | const docs: DocumentationPage = ` 24 | <page> 25 | <h1><title /></h1> 26 | <p>Windblade uses proportions instead of separete values.</p> 27 | 28 | <h2>Try it</h2> 29 | <try-it selected="$util"> 30 | <utils> 31 | <util> 32 | opacity- 33 | <select> 34 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 35 | <option value="$name" /> 36 | </for> 37 | </select> 38 | </util> 39 | </utils> 40 | 41 | <renderer html="${encodeString(` 42 | TODO 43 | `)}" /> 44 | 45 | <h3>Preview</h3> 46 | <viewport /> 47 | 48 | <h3>HTML</h3> 49 | <html /> 50 | 51 | <h3>Generated CSS</h3> 52 | <css /> 53 | </try-it> 54 | </page> 55 | ` 56 | 57 | return { rules, docs } 58 | } 59 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/filters.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationPage } from '@windblade/unocss-docs' 2 | 3 | export function dropShadow() { 4 | const docs: DocumentationPage = ` 5 | <page> 6 | <h1><title /></h1> 7 | <p>Drop shadows are removed for now because Tailwind\'s implementation is too limiting. Discussion in progress.</p> 8 | </page> 9 | ` 10 | 11 | return { rules: [], docs } 12 | } 13 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/flexboxAndGrid.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { size } = ruleUtils 8 | 9 | const nineChildren = [1, 2, 3, 4, 5, 6, 7, 8, 9].map(val => `<div class="bg-accent rounded-s.4 p-s flex items-center justify-center text-center">0${val}</div>`).join('\n') 10 | 11 | function generateAuto(ruleName: string, cssName: string) { 12 | const rules: Rule<Theme>[] = [ 13 | [`auto-${ruleName}-auto`, { [`grid-auto-${cssName}`]: 'auto' }], 14 | [`auto-${ruleName}-fr`, { [`grid-auto-${cssName}`]: 'minmax(0, 1fr)' }], 15 | size.rule(`auto-${ruleName}`, `grid-auto-${cssName}`), 16 | ] 17 | 18 | const docs: DocumentationPage = ` 19 | <page> 20 | <h1><title /></h1> 21 | <p>Added utilities for controlling the size of implicitly-created grid columns with proportion units.</p> 22 | 23 | <h2>Try it</h2> 24 | <try-it selected="$util"> 25 | <utils> 26 | <util>auto-${ruleName}-auto</util> 27 | <util>auto-${ruleName}-fr</util> 28 | <util> 29 | auto-${ruleName}- 30 | <select> 31 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 32 | <option value="$name"/> 33 | </for> 34 | </select> 35 | </util> 36 | </utils> 37 | 38 | <renderer html="${encodeString(` 39 | <div class="grid $util ${ruleName === 'cols' ? 'grid-flow-col' : 'grid-flow-row'} gap-s bg-accent-3 rounded-s.4 size-i-full"> 40 | ${nineChildren} 41 | </div> 42 | `)}" /> 43 | 44 | <h3>Preview</h3> 45 | <viewport /> 46 | 47 | <h3>HTML</h3> 48 | <html /> 49 | 50 | <h3>Generated CSS</h3> 51 | <css /> 52 | </try-it> 53 | </page> 54 | ` 55 | 56 | return { rules, docs } 57 | } 58 | 59 | export const gridAutoCols = () => generateAuto('cols', 'columns') 60 | export const gridAutoRows = () => generateAuto('rows', 'rows') 61 | 62 | function generateFitFill(type: 'fit' | 'fill', ruleName: string, cssName: string) { 63 | const rules: Rule<Theme>[] = [ 64 | size.rule(`grid-${type}-${ruleName}s`, `grid-template-${cssName}s`, { postprocess: size => (`repeat(auto-${type}, minmax(min(${size}, 100%), 1fr))`) }), 65 | ] 66 | 67 | const docs: DocumentationPage = ` 68 | <page> 69 | <h1><title /></h1> 70 | <p>Utilities specifying the columns in a grid layout using auto-${type}. Missing from Tailwind.</p> 71 | 72 | <h2>Try it</h2> 73 | <try-it selected="$util"> 74 | <utils> 75 | <util> 76 | grid-${type}-${ruleName}s- 77 | <select> 78 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 79 | <option value="$name"/> 80 | </for> 81 | </select> 82 | </util> 83 | </utils> 84 | 85 | <renderer html="${encodeString(` 86 | <div class="grid $util ${ruleName === 'row' ? 'grid-flow-col' : 'grid-flow-auto'} gap-s bg-accent-3 rounded-s.4 size-i-full"> 87 | ${nineChildren} 88 | </div> 89 | `)}" /> 90 | 91 | <h3>Preview</h3> 92 | <viewport /> 93 | 94 | <h3>HTML</h3> 95 | <html /> 96 | 97 | <h3>Generated CSS</h3> 98 | <css /> 99 | </try-it> 100 | </page> 101 | ` 102 | 103 | return { rules, docs } 104 | } 105 | 106 | export const gridFitCols = () => generateFitFill('fit', 'col', 'column') 107 | export const gridFillCols = () => generateFitFill('fill', 'col', 'column') 108 | export const gridFitRows = () => generateFitFill('fit', 'row', 'row') 109 | export const gridFillRows = () => generateFitFill('fill', 'row', 'row') 110 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/sizing.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | import { objectKeys } from 'ts-extras' 7 | import { iterObjects } from './doc-components' 8 | 9 | const { logical, size: sizes } = ruleUtils 10 | 11 | export function widthHeight() { 12 | const docs: DocumentationPage = ` 13 | <page> 14 | <h1><title /></h1> 15 | <p>Removed this, as well as min and max variants, in favor of the size counterparts</p> 16 | </page> 17 | ` 18 | 19 | return { rules: [], docs } 20 | } 21 | 22 | export function size() { 23 | const rules: Rule<Theme>[] = sizes.axisRules('size', '', '', 'size') 24 | 25 | const docs: DocumentationPage = ` 26 | <page> 27 | <h1><title /></h1> 28 | <p>Utilities for setting the size of an element. Missing from Tailwind.</p> 29 | 30 | <h2>Try it</h2> 31 | <try-it selected="$util"> 32 | <utils> 33 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 34 | <util renderer="${axis}"> 35 | size-${axis}- 36 | <select> 37 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 38 | <option value="$name"/> 39 | `)} 40 | </select> 41 | </util> 42 | `).join('')} 43 | </utils> 44 | 45 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 46 | <renderer for="${axis}" html="${encodeString(` 47 | <div class="$util ${axis === 'i' ? 'min-size-b-m.2' : 'min-size-i-m.2'} bg-accent rounded-s"></div> 48 | `)}" /> 49 | `).join('')} 50 | 51 | <h3>Preview</h3> 52 | <viewport /> 53 | 54 | <h3>HTML</h3> 55 | <html /> 56 | 57 | <h3>Generated CSS</h3> 58 | <css /> 59 | </try-it> 60 | </page> 61 | ` 62 | 63 | return { rules, docs } 64 | } 65 | 66 | function minMaxSizeHtml(axis: string) { 67 | return ` 68 | <div class="$util ${axis === 'inline' ? 'min-size-b-m.2' : 'min-size-i-m.2'} bg-accent rounded-s"></div> 69 | ` 70 | } 71 | 72 | export function minSize() { 73 | const rules: Rule<Theme>[] = sizes.axisRules('min-size', '', 'min', 'size') 74 | 75 | const docs: DocumentationPage = ` 76 | <page> 77 | <h1><title /></h1> 78 | <p>Utilities for setting the minimum size of an element. Missing from Tailwind.</p> 79 | 80 | <h2>Try it</h2> 81 | <try-it selected="$util"> 82 | <utils> 83 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 84 | <util renderer="${axis}"> 85 | min-size-${axis}- 86 | <select> 87 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 88 | <option value="$name" /> 89 | `)} 90 | </select> 91 | </util> 92 | `).join('')} 93 | </utils> 94 | 95 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 96 | <renderer for="${axis}" html="${encodeString(` 97 | ${minMaxSizeHtml(axis)} 98 | `)}" /> 99 | `).join('')} 100 | 101 | <h3>Preview</h3> 102 | <viewport /> 103 | 104 | <h3>HTML</h3> 105 | <html /> 106 | 107 | <h3>Generated CSS</h3> 108 | <css /> 109 | </try-it> 110 | </page> 111 | ` 112 | 113 | return { rules, docs } 114 | } 115 | 116 | export function maxSize() { 117 | const rules: Rule<Theme>[] = sizes.axisRules('max-size', '', 'max', 'size') 118 | 119 | const docs: DocumentationPage = ` 120 | <page> 121 | <h1><title /></h1> 122 | <p>Utilities for setting the maximum size of an element. Missing from Tailwind.</p> 123 | 124 | <h2>Try it</h2> 125 | <try-it selected="$util"> 126 | <utils> 127 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 128 | <util renderer="${axis}"> 129 | max-size-${axis}- 130 | <select> 131 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 132 | <option value="$name"/> 133 | `)} 134 | </select> 135 | </util> 136 | `).join('')} 137 | </utils> 138 | 139 | ${objectKeys(logical.abbreviations.axis).map(axis => ` 140 | <renderer for="${axis}" html="${encodeString(` 141 | ${minMaxSizeHtml(axis)} 142 | `)}" /> 143 | `).join('')} 144 | 145 | <h3>Preview</h3> 146 | <viewport /> 147 | 148 | <h3>HTML</h3> 149 | <html /> 150 | 151 | <h3>Generated CSS</h3> 152 | <css /> 153 | </try-it> 154 | </page> 155 | ` 156 | 157 | return { rules, docs } 158 | } 159 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/spacing.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | import { iterObjects, selectLogical } from './doc-components' 7 | 8 | const { size } = ruleUtils 9 | 10 | export function padding() { 11 | const rules: Rule<Theme>[] = size.edgeRules('p', '', 'padding', '') 12 | 13 | const docs: DocumentationPage = ` 14 | <page> 15 | <h1><title /></h1> 16 | <p>Replaced physical properties with logical.</p> 17 | 18 | <h2>Try it</h2> 19 | <try-it selected="$util"> 20 | <utils> 21 | <util> 22 | p- 23 | <select> 24 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 25 | <option value="$name"/> 26 | `)} 27 | </select> 28 | </util> 29 | <util> 30 | p- 31 | ${selectLogical({ axis: true, edges: true })}- 32 | <select> 33 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 34 | <option value="$name"/> 35 | `)} 36 | </select> 37 | </util> 38 | </utils> 39 | 40 | <renderer html="${encodeString(` 41 | <div class="$util rounded-s bg-accent"> 42 | <div class="border border-dashed rounded-s.2">Padding</div> 43 | </div> 44 | `)}" /> 45 | 46 | <h3>Preview</h3> 47 | <viewport /> 48 | 49 | <h3>HTML</h3> 50 | <html /> 51 | 52 | <h3>Generated CSS</h3> 53 | <css /> 54 | </try-it> 55 | </page> 56 | ` 57 | 58 | return { rules, docs } 59 | } 60 | 61 | export function margin() { 62 | const rules: Rule<Theme>[] = size.edgeRules('m', '', 'margin', '') 63 | 64 | const docs: DocumentationPage = ` 65 | <page> 66 | <h1><title /></h1> 67 | <p>Replaced physical properties with logical.</p> 68 | 69 | <h2>Try it</h2> 70 | <try-it selected="$util"> 71 | <utils> 72 | <util> 73 | m- 74 | <select> 75 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 76 | <option value="$name"/> 77 | `)} 78 | </select> 79 | </util> 80 | <util> 81 | m- 82 | ${selectLogical({ axis: true, edges: true })}- 83 | <select> 84 | ${iterObjects(['theme.windblade.proportions', 'theme.windblade.miscSizes'], '$name', '$val', ` 85 | <option value="$name"/> 86 | `)} 87 | </select> 88 | </util> 89 | </utils> 90 | 91 | <renderer html="${encodeString(` 92 | <div class="border border-dashed border-color-accent rounded-s.2"> 93 | <div class="$util p-s rounded-s bg-accent">Margin</div> 94 | </div> 95 | `)}" /> 96 | 97 | <h3>Preview</h3> 98 | <viewport /> 99 | 100 | <h3>HTML</h3> 101 | <html /> 102 | 103 | <h3>Generated CSS</h3> 104 | <css /> 105 | </try-it> 106 | </page> 107 | ` 108 | 109 | return { rules, docs } 110 | } 111 | 112 | export function spaceBetween() { 113 | const docs: DocumentationPage = ` 114 | <page> 115 | <h1><title /></h1> 116 | <p>Removed this. Please use gap and flex/grid/columns instead.</p> 117 | </page> 118 | ` 119 | 120 | return { rules: [], docs } 121 | } 122 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/svg.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { size } = ruleUtils 8 | 9 | export function strokeWidth() { 10 | const rules: Rule<Theme>[] = [size.rule('stroke', 'stroke-width')] 11 | 12 | const docs: DocumentationPage = ` 13 | <page> 14 | <h1><title /></h1> 15 | <p>Windblade proportions are used instead of separate size values.</p> 16 | 17 | <h2>Try it</h2> 18 | <try-it selected="$util"> 19 | <utils> 20 | <util> 21 | stroke- 22 | <select> 23 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 24 | <option value="$name" /> 25 | </for> 26 | </select> 27 | </util> 28 | </utils> 29 | 30 | <renderer html="${encodeString(` 31 | TODO 32 | `)}" /> 33 | 34 | <h3>Preview</h3> 35 | <viewport /> 36 | 37 | <h3>HTML</h3> 38 | <html /> 39 | 40 | <h3>Generated CSS</h3> 41 | <css /> 42 | </try-it> 43 | </page> 44 | ` 45 | 46 | return { rules, docs } 47 | } 48 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/tables.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { size } = ruleUtils 8 | 9 | export function borderSpacing() { 10 | const rules: Rule<Theme>[] = [ 11 | size.rule('border-spacing', 'border-spacing'), 12 | // we are skiping border-spacing-b and borer-spacing-i for now beccause they are hard to implement 13 | ] 14 | 15 | const docs: DocumentationPage = ` 16 | <page> 17 | <h1><title /></h1> 18 | <p>Changing border-spacing for individual axis is not possible at the moment.</p> 19 | 20 | <h2>Try it</h2> 21 | <try-it selected="$util"> 22 | <utils> 23 | <util> 24 | border-spacing- 25 | <select> 26 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 27 | <option value="$name" /> 28 | </for> 29 | </select> 30 | </util> 31 | </utils> 32 | 33 | <renderer html="${encodeString(` 34 | TODO 35 | `)}" /> 36 | 37 | <h3>Preview</h3> 38 | <viewport /> 39 | 40 | <h3>HTML</h3> 41 | <html /> 42 | 43 | <h3>Generated CSS</h3> 44 | <css /> 45 | </try-it> 46 | </page> 47 | ` 48 | 49 | return { rules, docs } 50 | } 51 | -------------------------------------------------------------------------------- /packages/unocss-preset/src/rules/documented/transforms.ts: -------------------------------------------------------------------------------- 1 | import type { Rule } from '@unocss/core' 2 | import type { DocumentationPage } from '@windblade/unocss-docs' 3 | import { encodeString } from '@windblade/unocss-docs' 4 | import { ruleUtils } from '@windblade/core' 5 | import type { Theme } from '@windblade/core' 6 | 7 | const { size } = ruleUtils 8 | 9 | export function scale() { 10 | const rules: Rule<Theme>[] = [size.rule('scale', 'transform', { defaultUnit: '', postprocess: val => `scale${val}` })] 11 | 12 | const docs: DocumentationPage = ` 13 | <page> 14 | <h1><title /></h1> 15 | <p>X and Y variants have been removed because they are not logical properties. Windblade also uses proportions instead of separete values.</p> 16 | 17 | <h2>Try it</h2> 18 | <try-it selected="$util"> 19 | <utils> 20 | <util> 21 | scale- 22 | <select> 23 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 24 | <option value="$name" /> 25 | </for> 26 | </select> 27 | </util> 28 | </utils> 29 | 30 | <renderer html="${encodeString(` 31 | TODO 32 | `)}" /> 33 | 34 | <h3>Preview</h3> 35 | <viewport /> 36 | 37 | <h3>HTML</h3> 38 | <html /> 39 | 40 | <h3>Generated CSS</h3> 41 | <css /> 42 | </try-it> 43 | </page> 44 | ` 45 | 46 | return { rules, docs } 47 | } 48 | 49 | export function rotate() { 50 | const rules: Rule<Theme>[] = [size.rule('rotate', 'transform', { defaultUnit: 'deg', postprocess: val => `rotate${Number(val) * 360}` })] 51 | 52 | const docs: DocumentationPage = ` 53 | <page> 54 | <h1><title /></h1> 55 | <p>Windblade uses proportions instead of separete values.</p> 56 | 57 | <h2>Try it</h2> 58 | <try-it selected="$util"> 59 | <utils> 60 | <util> 61 | rotate- 62 | <select> 63 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 64 | <option value="$name" /> 65 | </for> 66 | </select> 67 | </util> 68 | </utils> 69 | 70 | <renderer html="${encodeString(` 71 | TODO 72 | `)}" /> 73 | 74 | <h3>Preview</h3> 75 | <viewport /> 76 | 77 | <h3>HTML</h3> 78 | <html /> 79 | 80 | <h3>Generated CSS</h3> 81 | <css /> 82 | </try-it> 83 | </page> 84 | ` 85 | 86 | return { rules, docs } 87 | } 88 | 89 | export function translate() { 90 | const rules: Rule<Theme>[] = [size.rule('translate', 'transform', { postprocess: val => `translate${val}` })] 91 | 92 | const docs: DocumentationPage = ` 93 | <page> 94 | <h1><title /></h1> 95 | <p>X and Y variants have been removed because they are not logical properties. Windblade also uses proportions instead of separete values.</p> 96 | 97 | <h2>Try it</h2> 98 | <try-it selected="$util"> 99 | <utils> 100 | <util> 101 | translate- 102 | <select> 103 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 104 | <option value="$name" /> 105 | </for> 106 | </select> 107 | </util> 108 | </utils> 109 | 110 | <renderer html="${encodeString(` 111 | TODO 112 | `)}" /> 113 | 114 | <h3>Preview</h3> 115 | <viewport /> 116 | 117 | <h3>HTML</h3> 118 | <html /> 119 | 120 | <h3>Generated CSS</h3> 121 | <css /> 122 | </try-it> 123 | </page> 124 | ` 125 | 126 | return { rules, docs } 127 | } 128 | 129 | export function skew() { 130 | const rules: Rule<Theme>[] = [size.rule('skew', 'transform', { defaultUnit: 'deg', postprocess: val => `skew${Number(val) * 360}` })] 131 | 132 | const docs: DocumentationPage = ` 133 | <page> 134 | <h1><title /></h1> 135 | <p>Windblade uses proportions instead of separete values.</p> 136 | 137 | <h2>Try it</h2> 138 | <try-it selected="$util"> 139 | <utils> 140 | <util> 141 | skew- 142 | <select> 143 | <for object="theme.windblade.proportions" key-as="$name" value-as="$value"> 144 | <option value="$name" /> 145 | </for> 146 | </select> 147 | </util> 148 | </utils> 149 | 150 | <renderer html="${encodeString(` 151 | TODO 152 | `)}" /> 153 | 154 | <h3>Preview</h3> 155 | <viewport /> 156 | 157 | <h3>HTML</h3> 158 | <html /> 159 | 160 | <h3>Generated CSS</h3> 161 | <css /> 162 | </try-it> 163 | </page> 164 | ` 165 | 166 | return { rules, docs } 167 | } 168 | -------------------------------------------------------------------------------- /packages/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | -------------------------------------------------------------------------------- /packages/website/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="UTF-8" /> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 | <title>Windblade 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@windblade/website", 3 | "type": "module", 4 | "version": "2.0.0-beta.7", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "update-post": "vite build", 10 | "preview": "vite preview", 11 | "typecheck": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@solidjs/router": "^0.8.2", 15 | "@types/js-beautify": "^1.13.3", 16 | "@unocss/core": "0.55.0", 17 | "@windblade/core": "workspace:*", 18 | "@windblade/unocss-docs": "workspace:*", 19 | "@windblade/unocss-preset": "workspace:*", 20 | "highlight.js": "^11.8.0", 21 | "js-beautify": "^1.14.8", 22 | "solid-headless": "^0.13.1", 23 | "solid-js": "^1.7.7", 24 | "solid-transition-group": "^0.2.2", 25 | "xast-util-from-xml": "^3.0.0" 26 | }, 27 | "devDependencies": { 28 | "postcss": "^8.4.24", 29 | "postcss-preset-env": "^8.5.1", 30 | "ts-extras": "^0.11.0", 31 | "unocss-preset-mini-variants": "^0.53.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/website/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const env = require('postcss-preset-env') 2 | 3 | const config = { 4 | plugins: [ 5 | env({ 6 | stage: 3, 7 | features: { 8 | 'nesting-rules': true, 9 | }, 10 | }), 11 | ], 12 | } 13 | 14 | module.exports = config 15 | -------------------------------------------------------------------------------- /packages/website/src/App.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import Root from './router' 3 | import themeStore from '~/stores/themeStore' 4 | import docsStore from '~/stores/docsStore' 5 | 6 | const Main: Component = () => { 7 | docsStore.fetchIndex() 8 | const themeStyles = () => `scheme-${themeStore.scheme()}-${themeStore.hue()}` 9 | 10 | return ( 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | export default Main 18 | -------------------------------------------------------------------------------- /packages/website/src/api/core.ts: -------------------------------------------------------------------------------- 1 | export interface Success { 2 | success: true 3 | value: V 4 | } 5 | 6 | export type Option = Success | { 7 | success: false 8 | error: E 9 | } 10 | 11 | export interface ApiError { 12 | message: string 13 | cause: any 14 | } 15 | -------------------------------------------------------------------------------- /packages/website/src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | export * from './modules' 3 | -------------------------------------------------------------------------------- /packages/website/src/api/modules.tsx: -------------------------------------------------------------------------------- 1 | import type { Module, ModuleId, ModuleMeta } from './modules/types' 2 | import type { Option } from './core' 3 | import moduleDefs from './modules/data' 4 | 5 | export function getIndex(): Option, string> { 6 | try { 7 | const value = new Map() 8 | Object.entries(moduleDefs).forEach(([id, def]) => { 9 | value.set(id, def.meta) 10 | }) 11 | return { 12 | success: true, 13 | value, 14 | } 15 | } 16 | catch (err) { 17 | return { 18 | success: false, 19 | error: 'An unexpected error occurred', 20 | } 21 | } 22 | } 23 | 24 | export async function get(id: ModuleId): Promise> { 25 | try { 26 | const def = moduleDefs[id] 27 | return { 28 | success: true, 29 | value: { 30 | meta: def.meta, 31 | docs: await def.loadDocs(), 32 | }, 33 | } 34 | } 35 | catch (err) { 36 | return { 37 | success: false, 38 | error: `${err}`, 39 | } 40 | } 41 | } 42 | 43 | export * from './modules/types' 44 | -------------------------------------------------------------------------------- /packages/website/src/api/modules/data.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | import type { ModuleId, ModuleMeta } from './types' 3 | 4 | const defs: Record Promise 7 | }> = { 8 | complete: { 9 | meta: { 10 | title: 'Complete', 11 | description: 'Complete package intended to replace Tailwind or unocss-preset-wind. Not recommended at the moment', 12 | openOn: ['Usage', 'Installation'], 13 | }, 14 | loadDocs: async () => (await import('@windblade/unocss-preset')).docs.default, 15 | }, 16 | color: { 17 | meta: { 18 | title: 'Color', 19 | description: 'Semantic color utils from Windblade.', 20 | openOn: ['Usage', 'Installation'], 21 | }, 22 | loadDocs: async () => (await import('@windblade/unocss-preset-color')).docs.default, 23 | }, 24 | dollars: { 25 | meta: { 26 | title: 'Dollars', 27 | description: '$ syntax from Windblade.', 28 | openOn: ['Usage', 'Installation'], 29 | }, 30 | loadDocs: async () => (await import('@windblade/unocss-preset-dollars')).docs.default, 31 | }, 32 | } 33 | 34 | export default defs 35 | -------------------------------------------------------------------------------- /packages/website/src/api/modules/types.ts: -------------------------------------------------------------------------------- 1 | import type { DocumentationTree } from '@windblade/unocss-docs' 2 | 3 | export type ModuleId = string 4 | 5 | export interface ModuleMeta { 6 | title: string 7 | description: string 8 | openOn: string[] 9 | } 10 | 11 | export interface Module { 12 | meta: ModuleMeta 13 | docs: DocumentationTree 14 | } 15 | -------------------------------------------------------------------------------- /packages/website/src/components/DocsNav.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, ParentComponent } from 'solid-js' 2 | import { createContext, useContext } from 'solid-js' 3 | import type { DocumentationTree } from '@windblade/unocss-docs' 4 | 5 | import Selector from '~/components/ModuleSelector' 6 | import ForMap from '~/lib/ForMap' 7 | 8 | interface Settings { 9 | leafActive: (path: string[]) => boolean 10 | leafAs: ParentComponent<{ 11 | path: string[] 12 | class?: string 13 | }> 14 | } 15 | 16 | const Context = createContext() 17 | 18 | const Button: Component<{ 19 | path: string[] 20 | title: string 21 | i: number 22 | }> = (props) => { 23 | const settings = useContext(Context) as Settings 24 | 25 | const style = `filter: hue-rotate(${3.6 * props.i}deg);` 26 | const active = () => settings.leafActive(props.path) 27 | 28 | const styles = () => ({ 29 | root: ` 30 | block 31 | relative 32 | p-s.6 33 | p-i-s 34 | p-is-m.2 35 | rounded-full 36 | text-start 37 | justify-start 38 | transition 39 | ease-out 40 | overflow-hidden 41 | hover:bg-accent-3 42 | hover:text-fg-1 43 | ${active() ? 'bg-surface text-fg-1' : 'text-fg-3'} 44 | `, 45 | dot: { 46 | all: ` 47 | transition 48 | absolute 49 | size-b-m.2 50 | size-i-m.2 51 | rounded-full 52 | inset-0 53 | inset-b-0 54 | m-b-auto 55 | `, 56 | glow: ` 57 | blur-s 58 | ${active() ? 'bg-accent-2' : ''} 59 | `, 60 | fg: ` 61 | size-b-s.4 62 | size-i-s.4 63 | m-is-$(($m.2-$s.4)/2) 64 | ${active() ? 'bg-accent' : 'bg-accent-2'} 65 | `, 66 | }, 67 | }) 68 | 69 | return ( 70 | 74 |
75 |
76 |
77 |
78 | {props.title} 79 | 80 | ) 81 | } 82 | 83 | const Branch: Component<{ 84 | tree: DocumentationTree 85 | prefix: string[] 86 | depth: number 87 | }> = (props) => { 88 | let i = 0 89 | 90 | return (<> 91 |
    0 ? 'before:font-semibold before:m-be-s before:block gap-s.2' : 'gap-s'}`} title={props.depth > 0 ? props.prefix.at(-1) : undefined}> 92 | 93 | {([name, value]) => ( 94 |
  • 95 | {typeof value === 'string' 96 | ?
  • 100 | )} 101 |
    102 |
103 | ) 104 | } 105 | 106 | const Main: Component<{ 107 | tree: DocumentationTree 108 | class?: string 109 | ref?: HTMLElement 110 | settings: Settings 111 | }> = (props) => { 112 | return ( 113 | 119 | ) 120 | } 121 | 122 | export default Main 123 | -------------------------------------------------------------------------------- /packages/website/src/components/ModuleSelector.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { Show } from 'solid-js' 3 | import { useNavigate, useParams } from '@solidjs/router' 4 | 5 | import For from '~/lib/ForMap' 6 | import { getUid } from '~/lib/uid' 7 | import Select from '~/lib/Select' 8 | import Error from '~/lib/Error' 9 | import docsStore from '~/stores/docsStore' 10 | import type { ModuleId, ModuleMeta } from '~/api' 11 | 12 | const Form: Component<{ 13 | index: Map 14 | }> = (props) => { 15 | const id = getUid() 16 | const { moduleId } = useParams<{ moduleId: ModuleId }>() 17 | const navigate = useNavigate() 18 | 19 | return ( 20 |
21 | 22 | 39 |
40 | ) 41 | } 42 | 43 | const Main: Component = () => ( 44 | Index not loaded} 47 | keyed 48 | > 49 | {option => option.success 50 | ?
51 | : Error loading index 52 | } 53 | 54 | ) 55 | 56 | export default Main 57 | -------------------------------------------------------------------------------- /packages/website/src/components/MoludeList.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { Show } from 'solid-js' 3 | import ModuleList from '~/lib/ModuleList' 4 | import Error from '~/lib/Error' 5 | import docsStore from '~/stores/docsStore' 6 | 7 | const Main: Component = () => ( 8 | Module index not loaded} 11 | keyed 12 | > 13 | {option => option.success 14 | ? 15 | : Error loading index 16 | } 17 | 18 | ) 19 | 20 | export default Main 21 | -------------------------------------------------------------------------------- /packages/website/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import 'uno.css' 3 | import './style.css' 4 | import { render } from 'solid-js/web' 5 | import App from './App' 6 | 7 | render(() => , document.body as HTMLElement) 8 | -------------------------------------------------------------------------------- /packages/website/src/lib/Code.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import Error from '~/lib/Error' 3 | import libs from '~/lib/external' 4 | 5 | const { highlighter } = libs 6 | 7 | const main: Component<{ 8 | lang: string 9 | value: string 10 | }> = (props) => { 11 | const style = `bg-surface p-s rounded-s leading-$($s+$s.4) overflow-auto ${props.lang ?? ''}` 12 | 13 | if (!props.lang) 14 | return
{props.value}
15 | 16 | let highlighted = props.value 17 | try { 18 | highlighted = highlighter()?.highlight(props.value, { language: props.lang }).value ?? '' 19 | } 20 | catch (err: any) { 21 | return Failed highlighting code. {err.message} 22 | } 23 | 24 | return
25 | }
26 | 
27 | export default main
28 | 


--------------------------------------------------------------------------------
/packages/website/src/lib/Container.tsx:
--------------------------------------------------------------------------------
 1 | import type { ParentComponent } from 'solid-js'
 2 | 
 3 | const Main: ParentComponent<{ class?: string }> = (props) => {
 4 |   return (
 5 |     
6 |
7 | {props.children} 8 |
9 |
10 | ) 11 | } 12 | 13 | export default Main 14 | -------------------------------------------------------------------------------- /packages/website/src/lib/Error.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | import themeStore from '~/stores/themeStore' 3 | 4 | const main: ParentComponent = props => ( 5 |
6 | Error: {props.children} 7 |
8 | ) 9 | 10 | export default main 11 | -------------------------------------------------------------------------------- /packages/website/src/lib/ForMap.tsx: -------------------------------------------------------------------------------- 1 | import type { JSXElement } from 'solid-js' 2 | import { createEffect, createSignal, on } from 'solid-js' 3 | 4 | function Main(props: { 5 | each: Map 6 | children: (entry: [ K, V ]) => JSXElement 7 | }) { 8 | const [children, setChildren] = createSignal([]) 9 | 10 | createEffect(on(() => props.each, () => { 11 | const nextChildren: JSXElement = [] 12 | props.each.forEach((value, key) => { 13 | nextChildren.push(props.children([key, value])) 14 | }) 15 | setChildren(nextChildren) 16 | })) 17 | 18 | return <>{children} 19 | } 20 | 21 | export default Main 22 | -------------------------------------------------------------------------------- /packages/website/src/lib/ModuleCard.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { LocalLink } from './rotuer' 3 | import type { ModuleMeta } from '~/api' 4 | 5 | const Main: Component<{ 6 | meta: ModuleMeta 7 | href: string 8 | onInspect?: () => void 9 | }> = props => ( 10 | 16 | {/*
{props.meta.icon}
*/} 17 |
18 |
19 |

{props.meta.title}

20 |

{props.meta.description}

21 |
22 |
23 |
24 | ) 25 | 26 | export default Main 27 | -------------------------------------------------------------------------------- /packages/website/src/lib/ModuleList.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import ModuleCard from './ModuleCard' 3 | import For from './ForMap' 4 | import type { ModuleId, ModuleMeta } from '~/api/modules/types' 5 | 6 | const Main: Component<{ 7 | map: Map 8 | onInspect?: (id: ModuleId) => void 9 | }> = props => (<> 10 |
    11 | 12 | {([id, meta]) => ( 13 |
  • 14 | props.onInspect?.(id)}/> 15 |
  • 16 | )} 17 |
    18 |
19 | ) 20 | 21 | export default Main 22 | -------------------------------------------------------------------------------- /packages/website/src/lib/Select.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | 3 | const Main: ParentComponent<{ 4 | id?: string 5 | class?: string 6 | onChange?: (e: Event) => void 7 | }> = props => ( 8 |
9 | 12 |
13 |
14 | ) 15 | 16 | export default Main 17 | -------------------------------------------------------------------------------- /packages/website/src/lib/ShadowDom.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { createEffect, createSignal } from 'solid-js' 3 | 4 | const Main: Component<{ 5 | class?: string 6 | innerHTML: string 7 | }> = (props) => { 8 | const [shadowRoot, setShadowRoot] = createSignal() 9 | let previewContainer: HTMLDivElement | undefined 10 | 11 | // Keep shadowRoot in sync with container ref 12 | createEffect(() => { 13 | if (!previewContainer) { 14 | setShadowRoot(undefined) 15 | return 16 | } 17 | if (shadowRoot()) 18 | return 19 | setShadowRoot(previewContainer.attachShadow({ mode: 'open' })) 20 | }) 21 | 22 | // Keep shadow dom in sync with innerHTML prop 23 | createEffect(() => { 24 | const root = shadowRoot() 25 | if (!root) 26 | return 27 | root.innerHTML = props.innerHTML 28 | }) 29 | 30 | return ( 31 |
32 | ) 33 | } 34 | 35 | export default Main 36 | -------------------------------------------------------------------------------- /packages/website/src/lib/external.ts: -------------------------------------------------------------------------------- 1 | import { createResource, createRoot } from 'solid-js' 2 | 3 | function main() { 4 | const [formatter] = createResource(async () => (await import('js-beautify')).default) 5 | const [highlighter] = createResource(async () => (await import('highlight.js')).default) 6 | const [xml] = createResource(async () => (await import('xast-util-from-xml'))) 7 | return { highlighter, formatter, xml } 8 | } 9 | 10 | export default createRoot(main) 11 | -------------------------------------------------------------------------------- /packages/website/src/lib/rotuer/LocalLink.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | import { A } from '@solidjs/router' 3 | import type { IBaseProps } from '@ui/primitives/Button/Base' 4 | import Base from '@ui/primitives/Button/Base' 5 | 6 | type IMainProps = IBaseProps & { 7 | href: string 8 | onClick?: () => void 9 | activeClass?: string 10 | } 11 | 12 | const Main: ParentComponent = (props) => { 13 | return ( 14 | ( 19 | { 25 | props.onClick?.() 26 | }} 27 | > 28 | {baseProps.children} 29 | 30 | )} 31 | > 32 | {props.children} 33 | 34 | ) 35 | } 36 | 37 | export default Main 38 | -------------------------------------------------------------------------------- /packages/website/src/lib/rotuer/Outlet.tsx: -------------------------------------------------------------------------------- 1 | import type { Component, ParentComponent } from 'solid-js' 2 | import { Outlet } from '@solidjs/router' 3 | import { Transition } from 'solid-transition-group' 4 | 5 | const style = 'animation-duration-m.4' 6 | 7 | const Main: Component<{ 8 | class?: string 9 | as?: ParentComponent<{ 10 | class: string 11 | }> 12 | }> = (props) => { 13 | const className = () => `${props.class} relative size-i-full size-b-full overflow-hidden` 14 | const Container = props.as ?? (props =>
{props.children}
) 15 | 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default Main 26 | -------------------------------------------------------------------------------- /packages/website/src/lib/rotuer/Page.tsx: -------------------------------------------------------------------------------- 1 | import type { ParentComponent } from 'solid-js' 2 | 3 | const Main: ParentComponent<{ 4 | class?: string 5 | ref?: HTMLDivElement 6 | }> = props => ( 7 |
8 | {props.children} 9 |
10 | ) 11 | 12 | export default Main 13 | -------------------------------------------------------------------------------- /packages/website/src/lib/rotuer/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Outlet } from './Outlet' 2 | export { default as LocalLink } from './LocalLink' 3 | export { default as Page } from './Page' 4 | export { default as spaIntegration } from './integration' 5 | -------------------------------------------------------------------------------- /packages/website/src/lib/rotuer/integration.ts: -------------------------------------------------------------------------------- 1 | import { createIntegration } from '@solidjs/router' 2 | 3 | function querySelector(selector: string) { 4 | // Guard against selector being an invalid CSS selector 5 | try { 6 | return document.querySelector(selector) 7 | } 8 | catch (e) { 9 | return null 10 | } 11 | } 12 | 13 | function scrollToHash(hash: string, fallbackTop?: boolean) { 14 | const el = querySelector(`#${hash}`) 15 | if (el) 16 | el.scrollIntoView() 17 | 18 | else if (fallbackTop) 19 | window.scrollTo(0, 0) 20 | } 21 | 22 | function bindEvent(target: EventTarget, type: string, handler: EventListener) { 23 | target.addEventListener(type, handler) 24 | return () => target.removeEventListener(type, handler) 25 | } 26 | 27 | export function main(fallback = '/') { 28 | const getCurentPath = () => { 29 | const queryString = window.location.search 30 | const urlParams = new URLSearchParams(queryString) 31 | const urlNav = decodeURIComponent(urlParams.get('navigation') ?? fallback) 32 | return urlNav 33 | } 34 | 35 | return createIntegration( 36 | () => ({ 37 | value: getCurentPath(), 38 | state: history.state, 39 | }), 40 | ({ value, replace, scroll, state }) => { 41 | const to = `?navigation=${encodeURIComponent(value)}` 42 | 43 | if (replace) 44 | window.history.replaceState(state, '', to) 45 | else 46 | window.history.pushState(state, '', to) 47 | 48 | scrollToHash(window.location.hash.slice(1), scroll) 49 | }, 50 | notify => bindEvent(window, 'popstate', () => notify()), 51 | { 52 | go: delta => window.history.go(delta), 53 | }, 54 | ) 55 | } 56 | 57 | export default main 58 | -------------------------------------------------------------------------------- /packages/website/src/lib/uid.ts: -------------------------------------------------------------------------------- 1 | let prevId = 0 2 | export const getUid = () => `uid-${++prevId}` 3 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page.tsx: -------------------------------------------------------------------------------- 1 | import { Show, useContext } from 'solid-js' 2 | import type { Element } from 'xast-util-from-xml/lib' 3 | 4 | import type { XmlComponent, XmlNodeRenderer } from './types' 5 | import { XmlContext, extendXmlContext } from './types' 6 | import { Context as RootContext } from './XmlRoot' 7 | import XmlChildren from './XmlChildren' 8 | import TryIt from './Page/TryIt' 9 | import ForUno from './Page/For' 10 | import Sample from './Page/Sample' 11 | import Example from './Page/Example' 12 | 13 | import Code from '~/lib/Code' 14 | import Error from '~/lib/Error' 15 | 16 | const render: XmlNodeRenderer = (node) => { 17 | const ctx = useContext(RootContext) 18 | 19 | switch (node.type) { 20 | case 'text': 21 | return node.value 22 | case 'element': 23 | switch (node.name) { 24 | case 'h1': 25 | return

26 | case 'h2': 27 | return

28 | case 'h3': 29 | return

30 | case 'title': 31 | return ctx?.title ?? No title 32 | case 'p': 33 | return ( 34 |

35 | 36 |

37 | ) 38 | case 'small': 39 | return ( 40 | 41 | 42 | 43 | ) 44 | case 'a': 45 | return ( 46 | 47 | 48 | 49 | ) 50 | case 'ul': 51 | return ( 52 |
    53 | 54 |
55 | ) 56 | case 'li': 57 | return ( 58 |
  • 59 | 60 |
  • 61 | ) 62 | case 'code': 63 | return ( 64 | 65 | 66 | 67 | ) 68 | case 'pre': 69 | return 70 | case 'example': 71 | return 72 | case 'try-it': 73 | return 74 | case 'for': 75 | return 76 | case 'sample': 77 | return ( 78 | 79 | {v => } 80 | 81 | ) 82 | default: 83 | return Unsupported XML element: {node.name} 84 | } 85 | default: 86 | return Unsupported XML node type: {node.type} 87 | } 88 | } 89 | 90 | const main: XmlComponent = (props) => { 91 | return ( 92 | 93 | 94 | 95 | ) 96 | } 97 | 98 | export default main 99 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/Example.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { createEffect, createSignal } from 'solid-js' 3 | import Viewport from './Viewport' 4 | import uno from '~/unocss' 5 | 6 | const main: Component<{ 7 | html: string 8 | }> = (props) => { 9 | const [css, setCss] = createSignal('') 10 | 11 | createEffect(async () => { 12 | setCss((await uno.generate(props.html)).css) 13 | }) 14 | 15 | return 19 | } 20 | 21 | export default main 22 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/For.tsx: -------------------------------------------------------------------------------- 1 | import { For, useContext } from 'solid-js' 2 | import type { Element } from 'xast-util-from-xml/lib' 3 | 4 | import type { XmlComponent } from '~/lib/uno-xml/types' 5 | import VariableContext, { applyVars } from '~/lib/uno-xml/XmlVariables' 6 | import XmlChildren from '~/lib/uno-xml/XmlChildren' 7 | 8 | import Error from '~/lib/Error' 9 | import uno from '~/unocss' 10 | 11 | function navigateUnoConfig(path: string) { 12 | let destination: any = uno.config 13 | path.split('.').forEach(node => destination = destination[node]) 14 | return destination 15 | } 16 | 17 | const main: XmlComponent = (props) => { 18 | const parentContext = useContext(VariableContext) 19 | 20 | const noAttrs = '\'for\' requires an \'array\' or \'object\' attribute' 21 | const noValue = '\'for\' requires a \'value-as\' attribute' 22 | const noKey = '\'for\' with an \'object\' attribute requires a \'key-as\' attribute' 23 | 24 | const attrs = props.attributes 25 | if (!attrs) 26 | return {noAttrs} 27 | 28 | if (attrs.array) { 29 | if (!attrs.as) 30 | return {noValue} 31 | 32 | return 33 | {value => ( 34 | 35 | 36 | 37 | )} 38 | 39 | } 40 | 41 | if (attrs.object) { 42 | const valueAs = attrs['value-as'] 43 | if (!valueAs) 44 | return {noValue} 45 | 46 | const keyAs = attrs['key-as'] 47 | if (!keyAs) 48 | return {noKey} 49 | 50 | return 51 | {([key, value]) => ( 52 | 57 | 58 | 59 | )} 60 | 61 | } 62 | 63 | return {noAttrs} 64 | } 65 | 66 | export default main 67 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/Sample.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { useContext } from 'solid-js' 3 | import VariableContext from '~/lib/uno-xml/XmlVariables' 4 | 5 | const main: Component<{ 6 | var: string 7 | }> = (props) => { 8 | const vars = useContext(VariableContext) 9 | 10 | return `${vars?.[props.var]}` 11 | } 12 | 13 | export default main 14 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/TryIt.tsx: -------------------------------------------------------------------------------- 1 | import type { Accessor, Component } from 'solid-js' 2 | import { Show, createContext, createEffect, createSignal, useContext } from 'solid-js' 3 | import type { Element } from 'xast-util-from-xml/lib' 4 | import type { XmlComponent, XmlNodeRenderer } from '../types' 5 | import { XmlContext, extendXmlContext } from '../types' 6 | import XmlChildren from '../XmlChildren' 7 | import Utils from './TryIt/Utils' 8 | import Viewport from './Viewport' 9 | import libs from '~/lib/external' 10 | import uno from '~/unocss' 11 | 12 | const { formatter, highlighter } = libs 13 | 14 | export interface SelectedUtil { 15 | util: string 16 | renderer: string 17 | } 18 | 19 | export const Context = createContext<{ 20 | selectUtil: (val: SelectedUtil) => void 21 | selected: Accessor 22 | html: Accessor 23 | css: Accessor<{ shortCss: string; fullCss: string } | undefined> 24 | }>() 25 | 26 | const styles = { 27 | pre: 'bg-surface p-s rounded-s leading-$($s+$s.4) overflow-auto', 28 | } 29 | 30 | const NothingSelected: Component = () => ( 31 |
    32 |
    33 | Please select a utility first 34 |
    35 | ) 36 | 37 | const render: XmlNodeRenderer = (node) => { 38 | const ctx = useContext(Context) 39 | 40 | if (node.type === 'element') { 41 | switch (node.name) { 42 | case 'utils': 43 | return 44 | case 'renderer': 45 | return null 46 | case 'viewport': 47 | return }> 48 | 54 | 55 | case 'html': 56 | return } keyed> 57 | {({ util }) =>
    ${util}`)}
     60 |           />}
     61 |         
     62 |       case 'css':
     63 |         return }>
     64 |           
     68 |         
     69 |     }
     70 |   }
     71 | }
     72 | 
     73 | const main: XmlComponent = (props) => {
     74 |   const [selected, setSelected] = createSignal()
     75 | 
     76 |   const [html, setHtml] = createSignal('')
     77 |   const [css, setCss] = createSignal<{ shortCss: string; fullCss: string }>()
     78 | 
     79 |   createEffect(async () => {
     80 |     const shortCss = (await uno.generate(selected()?.util ?? '', { safelist: false, preflights: false, minify: true })).css
     81 |     const fullCss = (await uno.generate(html())).css
     82 |     setCss({ shortCss, fullCss })
     83 |   })
     84 | 
     85 |   const renderers = () => props.children.map((child) => {
     86 |     if (child.type !== 'element')
     87 |       return null
     88 | 
     89 |     if (child.name !== 'renderer')
     90 |       return null
     91 | 
     92 |     if (!child.attributes)
     93 |       return null
     94 | 
     95 |     if (!child.attributes.html)
     96 |       return null
     97 | 
     98 |     return [new RegExp(child.attributes.for ?? ''), child.attributes.html]
     99 |   }).filter(Boolean) as [RegExp, string][]
    100 | 
    101 |   const selectUtil = (val: SelectedUtil) => {
    102 |     setSelected(val)
    103 | 
    104 |     for (let i = 0; i < renderers().length; ++i) {
    105 |       const [matcher, output] = renderers()[i]
    106 |       if (matcher.test(val.renderer)) {
    107 |         setHtml(output.replaceAll('$util', val.util))
    108 |         return
    109 |       }
    110 |     }
    111 |   }
    112 | 
    113 |   return (
    114 |     
    115 |       
    116 |         
    117 |       
    118 |     
    119 |   )
    120 | }
    121 | 
    122 | export default main
    123 | 
    
    
    --------------------------------------------------------------------------------
    /packages/website/src/lib/uno-xml/Page/TryIt/Utils.tsx:
    --------------------------------------------------------------------------------
     1 | import type { Accessor, Setter } from 'solid-js'
     2 | import { createContext, createSignal, useContext } from 'solid-js'
     3 | import type { Element } from 'xast-util-from-xml/lib'
     4 | 
     5 | import Util from './Utils/Util'
     6 | 
     7 | import type { XmlComponent, XmlNodeRenderer } from '~/lib/uno-xml/types'
     8 | import { XmlContext, extendXmlContext } from '~/lib/uno-xml/types'
     9 | import XmlChildren from '~/lib/uno-xml/XmlChildren'
    10 | 
    11 | export const Context = createContext<{
    12 |   selected: Accessor
    13 |   setSelected: Setter
    14 | }>()
    15 | 
    16 | const styles = {
    17 |   tr: 'border border-color-transparent border-be-color-fg-5',
    18 |   th: 'p-b-s.6 text-start text-fg-3',
    19 | }
    20 | 
    21 | const render: XmlNodeRenderer = (node, i) => {
    22 |   const ctx = useContext(Context)
    23 | 
    24 |   if (node.type === 'element') {
    25 |     switch (node.name) {
    26 |       case 'util':
    27 |         return  ctx?.setSelected(i)} />
    28 |     }
    29 |   }
    30 | }
    31 | 
    32 | const main: XmlComponent = (props) => {
    33 |   const [selected, setSelected] = createSignal(-1)
    34 | 
    35 |   return (
    36 |     
    37 |       
    38 |         
    39 |           
    40 |             
    41 |               
    42 |               
    43 |             
    44 |           
    45 |           
    46 |             
    47 |           
    48 |         
    Utility
    49 |
    50 |
    51 | ) 52 | } 53 | 54 | export default main 55 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/TryIt/Utils/Input.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import { createEffect, createSignal } from 'solid-js' 3 | import Button from '@ui/primitives/Button' 4 | 5 | const main: Component<{ 6 | onChange: (value: string) => void 7 | }> = (props) => { 8 | const [val, setVal] = createSignal(1) 9 | 10 | const activate = () => props.onChange(`${val()}`) 11 | 12 | createEffect(activate) 13 | 14 | const buttonClasses = 'size-b-m.2 p-i-s.6 self-stretch' 15 | 16 | return ( 17 |
    18 | 19 | 20 | 21 |
    22 | ) 23 | } 24 | 25 | export default main 26 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/TryIt/Utils/Select.tsx: -------------------------------------------------------------------------------- 1 | import { Show } from 'solid-js' 2 | import type { Element } from 'xast-util-from-xml/lib' 3 | import type { Component } from 'solid-js' 4 | 5 | import type { XmlComponent, XmlNodeRenderer } from '~/lib/uno-xml/types' 6 | import { XmlContext, extendXmlContext } from '~/lib/uno-xml/types' 7 | import XmlChildren from '~/lib/uno-xml/XmlChildren' 8 | import { applyVars } from '~/lib/uno-xml/XmlVariables' 9 | 10 | import Error from '~/lib/Error' 11 | 12 | const Option: Component<{ 13 | value: string 14 | }> = (props) => { 15 | const value = () => applyVars(props.value) 16 | return ( 17 | 20 | ) 21 | } 22 | 23 | const render: XmlNodeRenderer = (node) => { 24 | switch (node.type) { 25 | case 'element': 26 | switch (node.name) { 27 | case 'option': 28 | return ( 29 | 'option' requires a 'value' attribute} 33 | > 34 | {value => 36 | ) 37 | } 38 | } 39 | } 40 | 41 | const main: XmlComponent void 43 | }> = (props) => { 44 | return ( 45 | 46 | 54 | 55 | ) 56 | } 57 | 58 | export default main 59 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/TryIt/Utils/Util.tsx: -------------------------------------------------------------------------------- 1 | import type { Accessor } from 'solid-js' 2 | import { createContext, createEffect, createSignal, useContext } from 'solid-js' 3 | import type { Element } from 'xast-util-from-xml/lib' 4 | import Button from '@ui/primitives/Button' 5 | 6 | import { Context as TryItContext } from '../../TryIt' 7 | import Select from './Select' 8 | import Input from './Input' 9 | 10 | import type { XmlComponent, XmlNodeRenderer } from '~/lib/uno-xml/types' 11 | import { XmlContext, extendXmlContext } from '~/lib/uno-xml/types' 12 | import XmlChildren from '~/lib/uno-xml/XmlChildren' 13 | 14 | type PartSetter = (i: number, val: string) => void 15 | 16 | export const Context = createContext<{ 17 | parts: Accessor 18 | setPart: PartSetter 19 | select: () => void 20 | }>() 21 | 22 | const render: XmlNodeRenderer = (node, i) => { 23 | const ctx = useContext(Context) 24 | 25 | const select = (val: string) => { 26 | ctx?.setPart(i, val) 27 | ctx?.select() 28 | } 29 | 30 | switch (node.type) { 31 | case 'text': 32 | return ( 33 | 40 | ) 41 | case 'element': 42 | switch (node.name) { 43 | case 'select': 44 | return select(val)} /> 47 | } 48 | } 49 | } 50 | 51 | const main: XmlComponent void 54 | }> = (props) => { 55 | const tryItCtx = useContext(TryItContext) 56 | 57 | const [parts, setParts] = createSignal([]) 58 | 59 | const setPart: PartSetter = (i, val) => { 60 | setParts((prev) => { 61 | prev[i] = val 62 | return [...prev] 63 | }) 64 | } 65 | 66 | // Keep parts in sync with inner text nodes 67 | createEffect(() => { 68 | props.children.forEach((node, i) => { 69 | switch (node.type) { 70 | case 'text': 71 | setPart(i, node.value) 72 | break 73 | } 74 | }) 75 | }) 76 | 77 | const util = () => { 78 | const s = parts().join('').match(/\S+/g) 79 | return s ? s.join('') : '' 80 | } 81 | 82 | const select = () => { 83 | tryItCtx?.selectUtil({ util: util(), renderer: props.attributes?.renderer ?? '' }) 84 | props.onSelect() 85 | } 86 | 87 | return ( 88 | 89 | 90 | 91 | 92 |
    93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ) 101 | } 102 | 103 | export default main 104 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/Page/Viewport.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | import ShadowDom from '~/lib/ShadowDom' 3 | import themeStore from '~/stores/themeStore' 4 | 5 | const Main: Component<{ 6 | class?: string 7 | rootStyle?: string 8 | html: string 9 | css: string 10 | }> = (props) => { 11 | return 17 | 18 | ${props.html} 19 |
    20 | `} /> 21 | } 22 | 23 | export default Main 24 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/XmlChildren.tsx: -------------------------------------------------------------------------------- 1 | import { For } from 'solid-js' 2 | import type { Element } from 'xast-util-from-xml/lib' 3 | 4 | import type { XmlComponent } from './types' 5 | import { useXmlContext } from './types' 6 | 7 | const main: XmlComponent = (props) => { 8 | const context = useXmlContext() 9 | 10 | return ( 11 | 12 | {(node, i) => { 13 | for (let j = context.length - 1; j >= 0; --j) { 14 | const render = context[j] 15 | const JSX = render(node, i()) 16 | if (JSX !== undefined) 17 | return JSX 18 | } 19 | }} 20 | 21 | ) 22 | } 23 | 24 | export default main 25 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/XmlRoot.tsx: -------------------------------------------------------------------------------- 1 | import { For, createContext } from 'solid-js' 2 | 3 | import type { XmlRootComponent } from './types' 4 | import Page from './Page' 5 | 6 | import Error from '~/lib/Error' 7 | 8 | export const Context = createContext<{ 9 | title: string 10 | }>() 11 | 12 | const main: XmlRootComponent<{ 13 | title: string 14 | }> = (props) => { 15 | return ( 16 | 17 | 18 | {(node) => { 19 | switch (node.type) { 20 | case 'element': 21 | switch (node.name) { 22 | case 'page': 23 | return 24 | } 25 | } 26 | 27 | return Only 'page' tag is recognized as the page root 28 | }} 29 | 30 | 31 | ) 32 | } 33 | 34 | export default main 35 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/XmlVariables.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'solid-js' 2 | 3 | const main = createContext>() 4 | 5 | export function applyVars(input: string): string { 6 | let output = input 7 | const context = useContext(main) ?? {} 8 | Object.entries(context).forEach(([key, value]) => { 9 | output = output.replaceAll(key, value) 10 | }) 11 | return output 12 | } 13 | 14 | export default main 15 | -------------------------------------------------------------------------------- /packages/website/src/lib/uno-xml/types.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'solid-js' 2 | import type { Component, JSXElement } from 'solid-js' 3 | import type { Content, Root } from 'xast-util-from-xml/lib' 4 | 5 | // Old (components) 6 | 7 | export type XmlRootComponent

    = Component

    8 | 9 | export type XmlComponent

    = Component

    10 | 11 | // New (render functions) 12 | 13 | export type XmlNodeRenderer = (node: Content, i: number) => JSXElement 14 | 15 | export const XmlContext = createContext([]) 16 | 17 | export const useXmlContext = () => useContext(XmlContext) ?? {} 18 | 19 | export const XmlContextProvider = () => XmlContext.Provider 20 | 21 | export function extendXmlContext(extension: XmlNodeRenderer[]): XmlNodeRenderer[] { 22 | return [ 23 | ...useXmlContext(), 24 | ...extension, 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/docs/[id]/[...all].tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate, useParams } from '@solidjs/router' 2 | import Button from '@ui/primitives/Button' 3 | import type { DocumentationTree } from '@windblade/unocss-docs/src' 4 | import type { Component } from 'solid-js' 5 | import { Page } from '~/lib/rotuer' 6 | import docsStore from '~/stores/docsStore' 7 | 8 | function findFirstPage(docs: DocumentationTree, prefix: string[] = []): string[] | undefined { 9 | for (const [name, child] of docs.entries()) { 10 | if (typeof child === 'string') 11 | return [...prefix, name] 12 | 13 | const inChildren = findFirstPage(child, [...prefix, name]) 14 | 15 | if (inChildren) 16 | return inChildren 17 | } 18 | 19 | // No pages in this chapter 20 | return undefined 21 | } 22 | 23 | const Main: Component = () => { 24 | const params = useParams() 25 | const navigate = useNavigate() 26 | 27 | const tryOpenFirstPage = async () => { 28 | const mdle = await docsStore.getModuleById(params.moduleId) 29 | 30 | if (!mdle.success) 31 | return 32 | 33 | const pageAddress = findFirstPage(mdle.value.docs) 34 | 35 | if (pageAddress) 36 | navigate(`/docs/${params.moduleId}/${pageAddress.join('/')}`) 37 | } 38 | 39 | return ( 40 | 41 |

    Navigation error

    42 |

    43 | The page you are looking for is now a chapter. 44 |

    45 |

    46 | Use the navigation menu to open any page inside this chapter. 47 |

    48 | 51 | 52 | ) 53 | } 54 | 55 | export default Main 56 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/docs/[id]/[nav].tsx: -------------------------------------------------------------------------------- 1 | import { Show, Suspense } from 'solid-js' 2 | import type { Component } from 'solid-js' 3 | import { useParams } from '@solidjs/router' 4 | import type { DocumentationPage, DocumentationTree } from '@windblade/unocss-docs' 5 | 6 | import type { Module, ModuleId, Success } from '~/api' 7 | import docsStore from '~/stores/docsStore' 8 | import XmlRoot from '~/lib/uno-xml/XmlRoot' 9 | import Error from '~/lib/Error' 10 | import libs from '~/lib/external' 11 | import { Page } from '~/lib/rotuer' 12 | 13 | const DocPage: Component<{ 14 | title: string 15 | page: DocumentationPage 16 | }> = (props) => { 17 | return ( 18 | 19 | 20 | {(() => { 21 | const parser = libs.xml() 22 | if (!parser) 23 | return 'Error: Failed to load XML parser' 24 | 25 | let xml 26 | try { 27 | xml = parser.fromXml(props.page) 28 | } 29 | catch (err: unknown) { 30 | return Error parsing this page: {err as string} 31 | } 32 | 33 | if (xml) 34 | return 35 | 36 | return Error processing this page 37 | })()} 38 | 39 | 40 | ) 41 | } 42 | 43 | function navigateDocTree(docs: DocumentationTree, path: string[], i = 0): DocumentationPage | DocumentationTree | undefined { 44 | const nav = path[i] 45 | 46 | if (!nav) 47 | return docs 48 | 49 | const child = docs.get(decodeURIComponent(nav)) 50 | 51 | if (child instanceof Map) 52 | return navigateDocTree(child, path, ++i) 53 | 54 | return child 55 | } 56 | 57 | const Main: Component = () => { 58 | const params = useParams<{ 59 | moduleId: ModuleId 60 | l1: string 61 | l2: string 62 | l3: string 63 | l4: string 64 | l5: string 65 | l6: string 66 | }>() 67 | const mdle = docsStore.getCachedModuleById(params.moduleId) 68 | 69 | const someMdle = () => mdle.success 70 | const title = () => decodeURIComponent(params.l6 ?? params.l5 ?? params.l4 ?? params.l3 ?? params.l2 ?? params.l1) 71 | const value = () => navigateDocTree((mdle as Success).value.docs, [params.l1, params.l2, params.l3, params.l4, params.l5, params.l6]) 72 | 73 | return ( 74 | Error} 77 | > 78 | Not a page} 81 | > 82 | 83 | 84 | 85 | ) 86 | } 87 | 88 | export default Main 89 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/docs/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from 'solid-js' 2 | 3 | import Container from '~/lib/Container' 4 | import { Page } from '~/lib/rotuer' 5 | import Modules from '~/components/MoludeList' 6 | 7 | const Main: Component = () => { 8 | return ( 9 | 10 | 11 |

    Docs

    12 |

    Please select a Windblade module to see the documentation for.

    13 | 14 |
    15 |
    16 | ) 17 | } 18 | 19 | export default Main 20 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/calculations/tw.html.txt: -------------------------------------------------------------------------------- 1 |
    2 | Label 3 | 4 |
    5 | 6 | 7 | 8 |
    9 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/calculations/wb.html.txt: -------------------------------------------------------------------------------- 1 |
    2 | Label 3 | 4 |
    5 | 6 |
    7 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/colors/tw.html.txt: -------------------------------------------------------------------------------- 1 |
    Red
    2 |
    Green
    3 |
    Blue
    4 |
    Blue but text is desaturated
    5 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/colors/tw.js.txt: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | colors: { 4 | 'red': { 5 | 100: 'hsl(0, 60%, 10%)', 6 | 200: 'hsl(0, 60%, 20%)', 7 | 300: 'hsl(0, 60%, 30%)', 8 | 400: 'hsl(0, 60%, 40%)', 9 | 500: 'hsl(0, 60%, 50%)', 10 | 600: 'hsl(0, 60%, 60%)', 11 | 700: 'hsl(0, 60%, 70%)', 12 | 800: 'hsl(0, 60%, 80%)', 13 | 900: 'hsl(0, 60%, 90%)', 14 | }, 15 | 'desaturated-red': { 16 | 100: 'hsl(0, 20%, 10%)', 17 | 200: 'hsl(0, 20%, 20%)', 18 | 300: 'hsl(0, 20%, 30%)', 19 | 400: 'hsl(0, 20%, 40%)', 20 | 500: 'hsl(0, 20%, 50%)', 21 | 600: 'hsl(0, 20%, 60%)', 22 | 700: 'hsl(0, 20%, 70%)', 23 | 800: 'hsl(0, 20%, 80%)', 24 | 900: 'hsl(0, 20%, 90%)', 25 | }, 26 | 'green': { 27 | 100: 'hsl(120, 60%, 10%)', 28 | 200: 'hsl(120, 60%, 20%)', 29 | 300: 'hsl(120, 60%, 30%)', 30 | 400: 'hsl(120, 60%, 40%)', 31 | 500: 'hsl(120, 60%, 50%)', 32 | 600: 'hsl(120, 60%, 60%)', 33 | 700: 'hsl(120, 60%, 70%)', 34 | 800: 'hsl(120, 60%, 80%)', 35 | 900: 'hsl(120, 60%, 90%)', 36 | }, 37 | 'desaturated-green': { 38 | 100: 'hsl(120, 20%, 10%)', 39 | 200: 'hsl(120, 20%, 20%)', 40 | 300: 'hsl(120, 20%, 30%)', 41 | 400: 'hsl(120, 20%, 40%)', 42 | 500: 'hsl(120, 20%, 50%)', 43 | 600: 'hsl(120, 20%, 60%)', 44 | 700: 'hsl(120, 20%, 70%)', 45 | 800: 'hsl(120, 20%, 80%)', 46 | 900: 'hsl(120, 20%, 90%)', 47 | }, 48 | 'blue': { 49 | 100: 'hsl(240, 60%, 10%)', 50 | 200: 'hsl(240, 60%, 20%)', 51 | 300: 'hsl(240, 60%, 30%)', 52 | 400: 'hsl(240, 60%, 40%)', 53 | 500: 'hsl(240, 60%, 50%)', 54 | 600: 'hsl(240, 60%, 60%)', 55 | 700: 'hsl(240, 60%, 70%)', 56 | 800: 'hsl(240, 60%, 80%)', 57 | 900: 'hsl(240, 60%, 90%)', 58 | }, 59 | 'desaturated-blue': { 60 | 100: 'hsl(240, 20%, 10%)', 61 | 200: 'hsl(240, 20%, 20%)', 62 | 300: 'hsl(240, 20%, 30%)', 63 | 400: 'hsl(240, 20%, 40%)', 64 | 500: 'hsl(240, 20%, 50%)', 65 | 600: 'hsl(240, 20%, 60%)', 66 | 700: 'hsl(240, 20%, 70%)', 67 | 800: 'hsl(240, 20%, 80%)', 68 | 900: 'hsl(240, 20%, 90%)', 69 | }, 70 | }, 71 | }, 72 | }; 73 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/colors/wb.html.txt: -------------------------------------------------------------------------------- 1 |
    Red
    2 |
    Green
    3 |
    Blue
    4 |
    Blue but text is desaturated
    5 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/colors/wb.js.txt: -------------------------------------------------------------------------------- 1 | unocss({ 2 | theme: { 3 | windblade: { 4 | colors: { 5 | 'surface': { 6 | base: { dark: { l: 0.4, c: 0.04, a: 0.1 }} }, 7 | on: [ 8 | { dark: { l: 0.9, c: 0.1 } }, 9 | { dark: { l: 0.8, c: 0.1 } }, 10 | ] 11 | }, 12 | }, 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/fgColors/tw.html.txt: -------------------------------------------------------------------------------- 1 |
    Primary
    2 |
    Secondary
    3 |
    Tertiary
    4 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/fgColors/wb.html.txt: -------------------------------------------------------------------------------- 1 |
    Primary (text-fg-1 is applied by default)
    2 |
    Secondary
    3 |
    Tertiary
    4 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/js/tw.js.txt: -------------------------------------------------------------------------------- 1 | // Not possible 🙁 2 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/js/wb.js.txt: -------------------------------------------------------------------------------- 1 | import { getLCA, LCHToCSSColor } from "@windblade/core"; 2 | import { theme } from "@windblade/unocss-preset"; // this is just a source file and it does not know about your theme customizations. If you are using your own colors you should import them instead 3 | 4 | const brandHue = 80; 5 | 6 | getBrandColor((light?: boolean) => { 7 | const colors = getLCA(theme.windblade.colors['brand'].base); // returns light and dark variants with all values calculated 8 | 9 | let lca; 10 | if (light) { 11 | lca = colors.light; 12 | } else { 13 | lca = colors.dark; 14 | } 15 | 16 | return LCHToCSSColor(lca.l, lca.c, brandHue, lca.a).rgba; 17 | }); 18 | 19 | export default getBrandColor; 20 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/template/tw.html.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StarLederer/windblade/9b30c81e0d69ca6cdde5d2070d8a1a0b611fea0c/packages/website/src/router/pages/index/template/tw.html.txt -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/template/tw.js.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StarLederer/windblade/9b30c81e0d69ca6cdde5d2070d8a1a0b611fea0c/packages/website/src/router/pages/index/template/tw.js.txt -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/template/wb.html.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StarLederer/windblade/9b30c81e0d69ca6cdde5d2070d8a1a0b611fea0c/packages/website/src/router/pages/index/template/wb.html.txt -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/template/wb.js.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StarLederer/windblade/9b30c81e0d69ca6cdde5d2070d8a1a0b611fea0c/packages/website/src/router/pages/index/template/wb.js.txt -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/theme/tw.js.txt: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | spacing: { 4 | '0': '0', 5 | '0.25': '0.25rem', 6 | '0.5': '0.5rem', 7 | '0.75': '0.75rem', 8 | '1': '1rem', 9 | '2.5': '2.5rem', 10 | '5': '5rem', 11 | '7.5': '7.5rem', 12 | '10': '10rem', 13 | }, 14 | borderRadius: ({ theme }) => ({ 15 | DEFAULT: '0.5rem', 16 | ...theme('spacing') 17 | }), 18 | opacity: ({ theme }) => ({ 19 | ...theme('spacing') 20 | }), 21 | width: ({ theme }) => ({ 22 | ...theme('spacing') 23 | }), 24 | height: ({ theme }) => ({ 25 | ...theme('spacing') 26 | }), 27 | margin: ({ theme }) => ({ 28 | ...theme('spacing') 29 | }), 30 | borderWidth: ({ theme }) => ({ 31 | ...theme('spacing') 32 | }), 33 | // ... 34 | // ... 35 | // ... 36 | // ... 37 | // ... 38 | // ... 39 | // ... 40 | // ... 41 | // ... 42 | // ... 43 | // ... 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /packages/website/src/router/pages/index/theme/wb.js.txt: -------------------------------------------------------------------------------- 1 | unocss({ 2 | theme: { 3 | windblade: { 4 | proportions: { 5 | '0.25': 0.25, 6 | '0.5': 0.5, 7 | '0.75': 0.75, 8 | '1': 1, 9 | '2.5': 2, 10 | '5': 5, 11 | '7.5': 7.5, 12 | '10': 10, 13 | }, 14 | 15 | time: { 16 | baseUnitMs: 150, // you can use duration-0.25, duration-5, etc. where duration-1 is 150ms and the rest follows proportions 17 | }, 18 | }, 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /packages/website/src/stores/docsStore.ts: -------------------------------------------------------------------------------- 1 | import { createRoot, createSignal } from 'solid-js' 2 | 3 | import type { Module, ModuleId, ModuleMeta, Option } from '~/api' 4 | import { get, getIndex } from '~/api' 5 | 6 | function main() { 7 | const modules = new Map() 8 | 9 | const [index, setIndex] = createSignal, string>>() 10 | 11 | const getModuleById = async (id: ModuleId): Promise> => { 12 | const cached = modules.get(id) 13 | 14 | if (cached) { 15 | return { 16 | success: true, 17 | value: cached, 18 | } 19 | } 20 | else { 21 | const mdle = await get(id) 22 | 23 | if (mdle.success) 24 | modules.set(id, mdle.value) 25 | 26 | return mdle 27 | } 28 | } 29 | 30 | const getCachedModuleById = (id: ModuleId): Option => { 31 | const cached = modules.get(id) 32 | 33 | if (cached) { 34 | return { 35 | success: true, 36 | value: cached, 37 | } 38 | } 39 | else { 40 | return { 41 | success: false, 42 | error: 'Not cached', 43 | } 44 | } 45 | } 46 | 47 | const fetchIndex = async () => { 48 | setIndex(getIndex()) 49 | } 50 | 51 | return { index, fetchIndex, getModuleById, getCachedModuleById } 52 | } 53 | 54 | export default createRoot(main) 55 | -------------------------------------------------------------------------------- /packages/website/src/stores/themeStore.ts: -------------------------------------------------------------------------------- 1 | import { createMemo, createRoot, createSignal } from 'solid-js' 2 | 3 | type ColorScheme = 'light' | 'dark' 4 | 5 | export const hues: Record = { 6 | dark: 240, 7 | light: 260, 8 | } 9 | 10 | function main() { 11 | // System sceheme 12 | const [systemSceheme, setSystemScheme] = createSignal(window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark') 13 | window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (event) => { 14 | setSystemScheme(event.matches ? 'light' : 'dark') 15 | }) 16 | 17 | // Manually selected scheme 18 | const [enforceScheme, setEnforceScheme] = createSignal(undefined) 19 | const toggleScheme = () => { 20 | switch (enforceScheme()) { 21 | case 'dark': 22 | setEnforceScheme('light') 23 | break 24 | case 'light': 25 | setEnforceScheme('dark') 26 | break 27 | default: 28 | setEnforceScheme(systemSceheme() === 'light' ? 'dark' : 'light') 29 | } 30 | } 31 | 32 | // Computed 33 | const scheme = createMemo(() => enforceScheme() ?? systemSceheme() ?? 'dark') 34 | const hue = createMemo(() => scheme() === 'dark' ? hues.dark : hues.light) 35 | 36 | return { scheme, hue, enforceScheme, setEnforceScheme, toggleScheme } 37 | } 38 | 39 | export default createRoot(main) 40 | -------------------------------------------------------------------------------- /packages/website/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | cursor: inherit; 3 | } 4 | 5 | :root { 6 | font-family: 'Assistant', sans-serif; 7 | cursor: default; 8 | } 9 | 10 | a, 11 | button { 12 | cursor: pointer; 13 | } 14 | 15 | /* width */ 16 | ::-webkit-scrollbar { 17 | @apply size-i-s.2 size-b-s.2; 18 | } 19 | 20 | /* Track */ 21 | ::-webkit-scrollbar-track { 22 | @apply bg-fg-5; 23 | } 24 | 25 | /* Handle */ 26 | ::-webkit-scrollbar-thumb { 27 | @apply bg-fg-4 rounded-full; 28 | } 29 | 30 | /* Handle on hover */ 31 | ::-webkit-scrollbar-thumb:hover { 32 | @apply bg-fg-3; 33 | } 34 | 35 | :root { 36 | @apply accent-accent; 37 | } 38 | 39 | button { 40 | cursor: pointer; 41 | -webkit-tap-highlight-color: transparent; 42 | } 43 | 44 | html, 45 | body { 46 | block-size: 100%; 47 | } 48 | 49 | body { 50 | overflow: hidden; 51 | } 52 | 53 | .animate-in, 54 | .animate-out { 55 | animation-fill-mode: forwards; 56 | } 57 | 58 | .animate-in { 59 | animation-name: in; 60 | } 61 | 62 | .animate-out { 63 | animation-name: out; 64 | } 65 | 66 | pre, 67 | .font-mono { 68 | font-family: 'Cascadia Mono', monospace; 69 | } 70 | 71 | pre { 72 | cursor: text; 73 | @apply leading-[$($s)rem] text-s.8; 74 | } 75 | 76 | ul[title]::before { 77 | content: attr(title) 78 | } 79 | 80 | pre.css, 81 | pre.js, 82 | .hljs-comment, 83 | .hljs-tag { 84 | @apply text-fg-2; 85 | } 86 | 87 | .hljs-keyword, 88 | .hljs-variable, 89 | .hljs-title, 90 | .hljs-selector-class, 91 | .hljs-name { 92 | @apply text-accent; 93 | } 94 | 95 | .hljs-property, 96 | .hljs-attribute, 97 | .hljs-attr { 98 | @apply text-fg-1; 99 | } 100 | 101 | .hljs-string { 102 | @apply text-accent hue-rotate-s.2; 103 | } 104 | 105 | .hljs-number { 106 | @apply text-accent; 107 | } 108 | 109 | .hljs-comment { 110 | @apply text-fg-3; 111 | } 112 | 113 | 114 | @keyframes in { 115 | from { 116 | transform: scale(1.025); 117 | opacity: 0; 118 | } 119 | 120 | 50% { 121 | transform: scale(1.025); 122 | opacity: 0; 123 | } 124 | 125 | to { 126 | transform: scale(1); 127 | opacity: 1; 128 | } 129 | } 130 | 131 | @keyframes out { 132 | from { 133 | transform: scale(1); 134 | opacity: 1; 135 | } 136 | 137 | 50% { 138 | transform: scale(0.975); 139 | opacity: 0; 140 | } 141 | 142 | to { 143 | transform: scale(0.975); 144 | opacity: 0; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /packages/website/src/unocss.ts: -------------------------------------------------------------------------------- 1 | import { createGenerator } from '@unocss/core' 2 | import windblade from '@windblade/unocss-preset' 3 | import { createRoot } from 'solid-js' 4 | 5 | const uno = createRoot(() => createGenerator({ 6 | presets: [windblade()], 7 | safelist: [ 8 | 'scheme-dark-276', 9 | 'scheme-light-296', 10 | ], 11 | })) 12 | 13 | export default uno 14 | -------------------------------------------------------------------------------- /packages/website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@windblade/*": [ 9 | "../*" 10 | ], 11 | "@ui/*": [ 12 | "./submodules/ui/*" 13 | ], 14 | "~/*": [ 15 | "./src/*" 16 | ] 17 | }, 18 | "moduleResolution": "node", 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "jsx": "preserve", 22 | "jsxImportSource": "solid-js", 23 | "types": ["vite/client"], 24 | "noEmit": true, 25 | "isolatedModules": true, 26 | "skipDefaultLibCheck": true, 27 | "skipLibCheck": true 28 | }, 29 | "exclude": [ 30 | "dist/**", 31 | "node_modules/**", 32 | "submodules/**" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/website/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | import { defineConfig } from 'vite' 3 | import solidPlugin from 'vite-plugin-solid' 4 | import unocss from '@unocss/vite' 5 | import transformerDirective from '@unocss/transformer-directives' 6 | import presetIcons from '@unocss/preset-icons' 7 | import presetVariants from 'unocss-preset-mini-variants' 8 | import presetWindblade from '@windblade/unocss-preset' 9 | 10 | // import favicon from 'vite-plugin-favicons-inject' 11 | 12 | export default defineConfig({ 13 | base: './', 14 | 15 | resolve: { 16 | alias: { 17 | '@windblade/core': resolve('../core/src/index.ts'), 18 | '@windblade/unocss-docs': resolve('../unocss-docs/src/index.ts'), 19 | '@windblade/unocss-preset': resolve('../unocss-preset/src/index.ts'), 20 | '@windblade/unocss-preset-color': resolve('../unocss-preset-color/src/index.ts'), 21 | '@windblade/unocss-preset-dollars': resolve('../unocss-preset-dollars/src/index.ts'), 22 | 23 | '@windblade': resolve('..'), 24 | '@ui': resolve('./submodules/ui'), 25 | '~': resolve('./src'), 26 | }, 27 | }, 28 | 29 | plugins: [ 30 | // Breaks on GitHub actions for some reason 31 | // favicon( 32 | // resolve('../brand/logo.svg'), 33 | // { 34 | // icons: { 35 | // favicons: true, 36 | // android: false, 37 | // appleIcon: false, 38 | // appleStartup: false, 39 | // yandex: false, 40 | // windows: false, 41 | // }, 42 | // } 43 | // ), 44 | solidPlugin(), 45 | unocss({ 46 | presets: [ 47 | presetWindblade(), 48 | presetVariants(), 49 | presetIcons(), 50 | ], 51 | transformers: [ 52 | transformerDirective(), 53 | ], 54 | safelist: [ 55 | 'scheme-light-260', 56 | 'scheme-dark-240', 57 | ], 58 | rules: [ 59 | ['highlight', { filter: 'brightness(1.2) saturate(0.6)' }], 60 | ['highlight+', { filter: 'brightness(1.6) saturate(0.4)' }], 61 | ], 62 | }), 63 | ], 64 | }) 65 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - unocss-preset-windblade 3 | - 'packages/*' 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "lib": ["esnext"], 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "strict": true, 9 | "strictNullChecks": true, 10 | "resolveJsonModule": true, 11 | "skipDefaultLibCheck": true, 12 | "skipLibCheck": true, 13 | "jsx": "preserve", 14 | "types": [ 15 | "node", 16 | "vite/client" 17 | ], 18 | "paths": { 19 | "@windblade/core": ["./packages/core/src/index.ts"], 20 | "@windblade/website": ["./packages/website/src/index.ts"], 21 | "@windblade/unocss-preset": ["./packages/unocss-preset/src/index.ts"], 22 | "@windblade/unocss-preset-color": ["./packages/unocss-preset-color/src/index.ts"], 23 | "@windblade/unocss-preset-dollars": ["./packages/unocss-preset-dollars/src/index.ts"], 24 | "@windblade/unocss-docs": ["./packages/unocss-docs/src/index.ts"] 25 | } 26 | }, 27 | "exclude": [ 28 | "**/dist/**", 29 | "**/node_modules/**", 30 | "**/client/**", 31 | "**/playground/**", 32 | "**/examples/**", 33 | "**/fixtures/**", 34 | "**/interactive/**", 35 | "**/test/dts/**", 36 | "./packages/website" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /unocss-preset-windblade/README.md: -------------------------------------------------------------------------------- 1 |

    2 | 3 | Windblade 4 | 5 | 6 | Windblade 7 |

    8 | 9 |

    10 | Tailwind-inspired UnoCSS preset with a better color system, logical properties, and simpler customization. 11 |

    12 | 13 |

    14 | Homepage | Documentation (WIP) | UnoCSS | Tailwind 15 |

    16 | 17 | ## This package 18 | 19 | This is a wrapper package of [@windblade/unocss-preset][windblade] that exists mainly for legacy reasons. We recommend using [@windblade/unocss-preset][windblade] directly. 20 | 21 | [windblade]: https://www.npmjs.com/package/@windblade/unocss-preset 22 | -------------------------------------------------------------------------------- /unocss-preset-windblade/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unocss-preset-windblade", 3 | "version": "2.0.0-beta.5", 4 | "description": "Tailwind-inspired UnoCSS preset with considerable improvements", 5 | "author": "Star Lederer ", 6 | "license": "MIT", 7 | "funding": "https://github.com/sponsors/StarLederer", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/StarLederer/windblade.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/StarLederer/windblade/issues" 14 | }, 15 | "keywords": [ 16 | "unocss", 17 | "unocss-preset", 18 | "tailwind", 19 | "windblade" 20 | ], 21 | "sideEffects": false, 22 | "exports": { 23 | ".": { 24 | "types": "./dist/index.d.ts", 25 | "require": "./dist/index.cjs", 26 | "import": "./dist/index.mjs" 27 | }, 28 | "./*": "./*" 29 | }, 30 | "main": "dist/index.cjs", 31 | "module": "dist/index.mjs", 32 | "types": "dist/index.d.ts", 33 | "scripts": { 34 | "build": "unbuild", 35 | "stub": "unbuild --stub" 36 | }, 37 | "dependencies": { 38 | "@unocss/core": "0.55.0", 39 | "@windblade/core": "workspace:*", 40 | "@windblade/unocss-preset": "workspace:*", 41 | "@windblade/unocss-preset-color": "workspace:*", 42 | "@windblade/unocss-preset-dollars": "workspace:*" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /unocss-preset-windblade/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from '@windblade/unocss-preset' 2 | --------------------------------------------------------------------------------