├── .changeset ├── README.md └── config.json ├── .eslintignore ├── .eslintrc ├── .github ├── composite-actions │ └── install │ │ └── action.yml └── workflows │ ├── quality.yml │ ├── release.yml │ └── static.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── package.json ├── packages ├── config │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── types │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── variants │ ├── .gitignore │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ ├── create-svg.ts │ ├── index.ts │ └── resolve-icons │ │ ├── index.ts │ │ ├── resolve-collection-function.ts │ │ ├── resolve-collection-object.ts │ │ ├── resolve-collection-string.ts │ │ └── resolve-icon-entries.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── site ├── .gitignore ├── index.html ├── package.json ├── panda.config.ts ├── postcss.config.cjs ├── public └── vite.svg ├── src ├── App.tsx ├── index.css └── main.tsx ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with 4 | multi-package repos, or single-package repos to help you version and publish your code. You can find the full 5 | documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [ 11 | "sandbox-vite" 12 | ] 13 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.d.ts 2 | *.js 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 4 | "env": { 5 | "node": true, 6 | "browser": true 7 | }, 8 | "rules": { 9 | "no-prototype-builtins": "off", 10 | "@typescript-eslint/no-explicit-any": "off", 11 | "@typescript-eslint/explicit-module-boundary-types": "off", 12 | "@typescript-eslint/no-non-null-assertion": "off", 13 | "@typescript-eslint/ban-ts-comment": "off", 14 | "@typescript-eslint/no-var-requires": "off", 15 | "@typescript-eslint/ban-types": "off", 16 | "@typescript-eslint/no-unused-vars": [ 17 | "error", 18 | { 19 | "varsIgnorePattern": "^_", 20 | "argsIgnorePattern": "^_", 21 | "ignoreRestSiblings": true 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/composite-actions/install/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Install' 2 | description: 'Sets up Node.js and runs install' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - uses: pnpm/action-setup@v2.2.4 8 | with: 9 | version: 7 10 | 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v3 13 | with: 14 | node-version: 16.x 15 | registry-url: 'https://registry.npmjs.org' 16 | cache: 'pnpm' 17 | 18 | - name: Setup Git User 19 | shell: bash 20 | run: | 21 | git config --global user.email "anubra266@gmail.com" 22 | git config --global user.name "Abraham Anuoluwapo" 23 | 24 | - name: Install dependencies 25 | shell: bash 26 | run: pnpm install 27 | -------------------------------------------------------------------------------- /.github/workflows/quality.yml: -------------------------------------------------------------------------------- 1 | name: Quality 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} 14 | 15 | jobs: 16 | eslint: 17 | name: ESLint 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Install 24 | uses: ./.github/composite-actions/install 25 | 26 | - name: Run ESLint 27 | run: pnpm lint 28 | env: 29 | NODE_OPTIONS: "--max-old-space-size=4096" 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | paths: 6 | - ".changeset/**" 7 | - "packages/**" 8 | branches: 9 | - main 10 | 11 | jobs: 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Install 22 | uses: ./.github/composite-actions/install 23 | 24 | - name: Build packages 25 | run: pnpm build 26 | 27 | - name: Create release Pull Request or publish to NPM 28 | id: changesets 29 | uses: changesets/action@v1 30 | with: 31 | publish: pnpm release 32 | commit: "ci(changesets): version packages" 33 | setupGitUser: false 34 | env: 35 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | - name: Release to dev tag 40 | if: steps.changesets.outputs.published != 'true' 41 | run: | 42 | git checkout main 43 | pnpm changeset version --snapshot dev 44 | pnpm changeset publish --tag dev 45 | env: 46 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy Demo page 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | env: 24 | SITE_PATH: "site" 25 | 26 | jobs: 27 | build: 28 | name: Build 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Install 34 | uses: ./.github/composite-actions/install 35 | - name: Setup Node 36 | uses: actions/setup-node@v3 37 | - name: Setup Pages 38 | id: pages 39 | uses: actions/configure-pages@v2 40 | - name: Build with Vite 41 | run: | 42 | pnpm build 43 | working-directory: ${{ env.SITE_PATH }} 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v1 46 | with: 47 | path: ${{ env.SITE_PATH }}/dist_site 48 | 49 | deploy: 50 | environment: 51 | name: github-pages 52 | url: ${{ steps.deployment.outputs.page_url }} 53 | needs: build 54 | runs-on: ubuntu-latest 55 | name: Deploy 56 | steps: 57 | - name: Deploy to GitHub Pages 58 | id: deployment 59 | uses: actions/deploy-pages@v1 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | node_modules 3 | dist 4 | build 5 | dist_site -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | enable-pre-post-scripts=true -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | .next 5 | build 6 | *.hbs 7 | *.d.ts 8 | site/dist_site 9 | site/styled-system 10 | .github 11 | .changeset -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "bracketSpacing": true, 5 | "jsxSingleQuote": false, 6 | "proseWrap": "always", 7 | "semi": false, 8 | "tabWidth": 2, 9 | "trailingComma": "all" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 anubra266 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 |
4 | 😌 5 |
6 | css-picons 7 |
8 |
9 |

10 |
11 |
12 | 13 | npm package 14 | 15 | 16 | npm  downloads 17 | 18 | 19 | NPM 20 | 21 | 22 | 23 | GitHub Repo stars 24 | 25 |
26 |
27 | 28 | Use any icons with Pure CSS in CSS Panda 29 | 30 |
31 |
32 | 33 |
34 | 35 | ## About 36 | 37 | Use any icons with Pure CSS in [CSS Panda](https://github.com/chakra-ui/panda). 'Twas Inspired by 38 | [@unocss/preset-icons](https://github.com/unocss/unocss/tree/main/packages/preset-icons/) 39 | 40 | ## Install 41 | 42 | ```sh 43 | npm i -D @css-picons/config @iconify-json/[the-collection-you-want] 44 | ``` 45 | 46 | We use [Iconify](https://iconify.design) as our data source of icons. You need to install the corresponding iconset in 47 | `devDependencies` by following the `@iconify-json/*` pattern. For example, `@iconify-json/mdi` for 48 | [Material Design Icons](https://materialdesignicons.com/), `@iconify-json/tabler` for 49 | [Tabler](https://tabler-icons.io/). You can refer to [Icônes](https://icones.js.org/) or 50 | [Iconify](https://icon-sets.iconify.design/) for all the collections available. 51 | 52 | If you prefer to install the all the icon sets available on Iconify at once (~130MB): 53 | 54 | ```bash 55 | npm i -D @iconify/json 56 | ``` 57 | 58 | > Even if you do this, only the ones used in your code will be packaged by panda. That's nice. 59 | 60 | ## Configuration 61 | 62 | In your `panda.config.*` file, import `cssPicons` from `@css-picons/config`, then add it to presets 63 | 64 | ```ts 65 | import { cssPicons } from '@css-picons/config' 66 | export default defineConfig({ 67 | presets: [ 68 | // ... 69 | cssPicons({ 70 | // Specify all the icon sets you'll be using 71 | collections: ['mdi'], 72 | }), 73 | ], 74 | }) 75 | ``` 76 | 77 | Then make sure your `theme` is extendable, if you have configured it. 78 | 79 | ```js 80 | // panda.config.* 81 | export default defineConfig({ 82 | //... 83 | theme: { 84 | extend: { 85 | // ... 86 | }, 87 | }, 88 | }) 89 | ``` 90 | 91 | ## Usage 92 | 93 | Now you can use it in your components in all the ways `css-panda` allows through the `icon` recipe which is exported 94 | from your design system. The icon name follows the format `:` 95 | 96 | ```js 97 | import { icon } from '../panda/recipes' 98 | return ( 99 |
108 | ) 109 | ``` 110 | 111 | **Result**: 112 | 113 | 114 | 115 | #### Usage in other ways 116 | 117 | ```js 118 | // A basic anchor icon from Phosphor icons 119 | const className = icon({ name: 'ph:anchor-simple-thin' }) 120 | // An orange alarm from Material Design Icons 121 | const className = cx(icon({ name: 'mdi:alarm' }), css({ color: 'orange.400' })) 122 | // A large react logo 123 | const className = cx(icon({ name: 'logos:react' }), css({ fontSize: '3xl' })) 124 | // Sun in light mode, Moon in dark mode, from Carbon 125 | const className = icon({ name: { base: 'carbon:sun', _dark: 'carbon:moon' } }) 126 | // Twemoji of laugh, turns to tear on hovering 127 | const className = icon({ 128 | name: { 129 | base: 'twemoji:grinning-face-with-smiling-eyes', 130 | _hover: 'twemoji:face-with-tears-of-joy', 131 | }, 132 | }) 133 | ``` 134 | 135 | ### JSX 136 | 137 | When using in `jsx` you might want to use it in a reusable component, in such cases you have to tell `css-panda` the 138 | name of the component so it can watch it when generating styles. An example: 139 | 140 | ```js 141 | function AppIcon(props: IconVariants) { 142 | return ( 143 |
151 | ) 152 | } 153 | // Somewhere else 154 | return 155 | ``` 156 | 157 | You tell `panda` to watch the external component by using the `jsx` key in the config. 158 | 159 | ```ts 160 | import { cssPicons } from '@css-picons/config' 161 | export default defineConfig({ 162 | presets: [ 163 | // ... 164 | cssPicons({ 165 | // ... 166 | jsx: 'AppIcon', 167 | }), 168 | ], 169 | }) 170 | ``` 171 | 172 | **Note:** Panda automatically tracks components named as the capitalized version of the identifier. e.g. If your 173 | identifier is daocons, a component called `Daocons` that consumes it will be automatically tracked. In this case, we 174 | automatically track components named `Icon`. 175 | 176 | ### Identifier 177 | 178 | By default, you export `icon` from recipes which is then consumed to render the icons. But you can change this in the 179 | config with the `identifier` key. 180 | 181 | ```ts 182 | import { cssPicons } from '@css-picons/config' 183 | export default defineConfig({ 184 | presets: [ 185 | // ... 186 | cssPicons({ 187 | // ... 188 | identifier: 'cssIcon', 189 | }), 190 | ], 191 | }) 192 | ``` 193 | 194 | Then export and use the new identifier in your project instead. 195 | 196 | ```js 197 | import { cssIcon } from '../panda/recipes' 198 | return ( 199 |
208 | ) 209 | ``` 210 | 211 | ### Extra Styles 212 | 213 | You can provide extra styles to control the default behavior of the icons. You could make icons vertically aligned and 214 | inline by default by the following example: 215 | 216 | ```ts 217 | import { cssPicons } from '@css-picons/config' 218 | export default defineConfig({ 219 | presets: [ 220 | // ... 221 | cssPicons({ 222 | // ... 223 | styles: { 224 | verticalAlign: 'middle', 225 | display: 'inline-block', 226 | }, 227 | }), 228 | ], 229 | }) 230 | ``` 231 | 232 | ### Modes Overriding 233 | 234 | By default, `css-picons` will choose the rendering modes automatically for each icon based on the icons' 235 | characteristics. You can read more in this [blog post](https://antfu.me/posts/icons-in-pure-css). In some cases, you may 236 | want to explicitly set the rendering modes for each icon. 237 | 238 | - `?bg` for `background-img` - renders the icon as a background image 239 | - `?mask` for `mask` - renders the icon as a mask image 240 | 241 | For example, `vscode-icons:file-type-light-pnpm`, an icon with colors (the `svg` doesn't contain `currentColor`) that 242 | will be rendered as a background image. Use `vscode-icons:file-type-light-pnpm?mask` to render it as a mask image and 243 | bypass it's colors. 244 | 245 | #### Default mode 246 | 247 | You can also set the default mode in your config with the `mode` key. It's `auto` by default which means `css-picons` 248 | automatically selects a mode based on it's perception of the icon. 249 | 250 | ```ts 251 | export default defineConfig({ 252 | presets: [ 253 | // ... 254 | cssPicons({ 255 | // ... 256 | mode: 'mask', 257 | }), 258 | ], 259 | }) 260 | ``` 261 | 262 | ### Custom collections 263 | 264 | You can also provide your own custom collections by passing a tuple. 265 | 266 | The **first** item in the tuple is the collection's name. 267 | 268 | The **second** can either be a function or an object. 269 | 270 | **Basic example** 271 | 272 | ```ts 273 | export default defineConfig({ 274 | presets: [ 275 | // ... 276 | cssPicons({ 277 | // ... 278 | collections: [ 279 | // ... 280 | [ 281 | 'custom', 282 | { 283 | circle: '', 284 | }, 285 | ], 286 | ], 287 | }), 288 | ], 289 | }) 290 | ``` 291 | 292 | Then use it through the `custom` icon name: 293 | 294 | ```js 295 | import { icon } from '../panda/recipes' 296 | return
297 | ``` 298 | 299 | You can also provide a function that resolves to a string: 300 | 301 | **Read from file** 302 | 303 | ```ts 304 | export default defineConfig({ 305 | presets: [ 306 | // ... 307 | cssPicons({ 308 | // ... 309 | collections: [ 310 | // ... 311 | [ 312 | 'custom', 313 | { 314 | vite: () => fs.readFile('./public/vite.svg', 'utf-8'), 315 | }, 316 | ], 317 | ], 318 | }), 319 | ], 320 | }) 321 | ``` 322 | 323 | Usage: 324 | 325 | ```js 326 | import { icon } from '../panda/recipes' 327 | return
328 | ``` 329 | 330 | **Fetch icon svg from a remote server** 331 | 332 | ```ts 333 | // Native fetch is not defined in the node process 334 | import fetch from 'node-fetch' 335 | 336 | export default defineConfig({ 337 | presets: [ 338 | // ... 339 | cssPicons({ 340 | // ... 341 | collections: [ 342 | // ... 343 | [ 344 | 'solar', 345 | { 346 | 'airbuds-outline': async () => { 347 | return await fetch('https://api.iconify.design/solar:airbuds-outline.svg?color=%23888888').then((res) => 348 | res.text(), 349 | ) 350 | }, 351 | }, 352 | ], 353 | ], 354 | }), 355 | ], 356 | }) 357 | ``` 358 | 359 | Usage: 360 | 361 | ```js 362 | import { icon } from '../panda/recipes' 363 | return
364 | ``` 365 | 366 | **Iconify JSON or function returning iconify JSON** 367 | 368 | ```ts 369 | import feIconsData from '@iconify-json/fe/icons.json' 370 | import phIconsData from '@iconify-json/ph/icons.json' 371 | 372 | export default defineConfig({ 373 | presets: [ 374 | // ... 375 | cssPicons({ 376 | // ... 377 | collections: [ 378 | // ... 379 | ['fe', feIconsData], 380 | ['ph', () => phIconsData], 381 | ], 382 | }), 383 | ], 384 | }) 385 | ``` 386 | 387 | Usage: 388 | 389 | ```js 390 | import { icon } from '../panda/recipes' 391 | return
392 | return
393 | ``` 394 | 395 | **Dynamic import** 396 | 397 | ```ts 398 | export default defineConfig({ 399 | presets: [ 400 | // ... 401 | cssPicons({ 402 | // ... 403 | collections: [ 404 | // ... 405 | ['carbon', () => import('@iconify-json/carbon/icons.json').then((i) => i.default as any)], 406 | ], 407 | }), 408 | ], 409 | }) 410 | ``` 411 | 412 | Usage: 413 | 414 | ```js 415 | import { icon } from '../panda/recipes' 416 | return
417 | ``` 418 | 419 | **Fetch icons data from a remote server** 420 | 421 | ```ts 422 | // Native fetch is not defined in the node process 423 | 424 | export default defineConfig({ 425 | presets: [ 426 | // ... 427 | cssPicons({ 428 | // ... 429 | collections: [ 430 | // ... 431 | [ 432 | 'circle-flags', 433 | async () => { 434 | //* fetch iconifyJson from a remote server: 435 | //! We use node-fetch package because we can't access the native fetch 436 | return await fetch( 437 | 'https://raw.githubusercontent.com/iconify/icon-sets/master/json/circle-flags.json', 438 | ).then((res) => res.json()) 439 | }, 440 | ], 441 | ], 442 | }), 443 | ], 444 | }) 445 | ``` 446 | 447 | Usage: 448 | 449 | ```js 450 | import { icon } from '../panda/recipes' 451 | return
452 | ``` 453 | 454 | ### Transforming icons 455 | 456 | We provide a transform method which lets you transform icons when loading, for example adding fill attribute with 457 | currentColor. 458 | 459 | ```js 460 | export default defineConfig({ 461 | presets: [ 462 | // ... 463 | cssPicons({ 464 | // ... 465 | transform(svg) { 466 | return svg.replace(/#fff/, 'currentColor') 467 | }, 468 | }), 469 | ], 470 | }) 471 | ``` 472 | 473 | We also provide the `collection` and `icon` so you can streamline your transformations. 474 | 475 | ```js 476 | export default defineConfig({ 477 | presets: [ 478 | // ... 479 | cssPicons({ 480 | // ... 481 | transform(svg) { 482 | // do not apply fill to this icons on this collection 483 | if (collection === 'custom' && icon === 'circle') return svg 484 | return svg.replace(/#fff/, 'currentColor') 485 | }, 486 | }), 487 | ], 488 | }) 489 | ``` 490 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-picons", 3 | "version": "0.0.1", 4 | "description": "Pure CSS icons for css panda", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepare": "pnpm build", 8 | "dev": "pnpm -r --parallel --filter=./packages/** dev", 9 | "build:types": "pnpm --filter=./packages/types build", 10 | "build:variants": "pnpm --filter=./packages/variants build", 11 | "build:config": "pnpm --filter=./packages/config build", 12 | "build": "pnpm build:types && pnpm build:variants && pnpm build:config", 13 | "check": "pnpm build && pnpm typecheck && pnpm lint && pnpm test run", 14 | "clean": "pnpm -r --parallel exec rm -rf dist .turbo *.log", 15 | "reset": "pnpm -r --parallel exec rm -rf node_modules && rm -rf node_modules", 16 | "lint": "eslint packages --ext .ts", 17 | "prettier": "prettier --check .", 18 | "prettier-fix": "prettier --write .", 19 | "version": "changeset version", 20 | "release": "changeset publish", 21 | "release-dev": "changeset version --snapshot dev && changeset publish --tag dev" 22 | }, 23 | "keywords": [], 24 | "author": "Abraham A. ", 25 | "license": "MIT", 26 | "dependencies": { 27 | "@changesets/changelog-github": "0.4.8", 28 | "@changesets/cli": "2.26.1", 29 | "@types/node": "20.1.1", 30 | "@typescript-eslint/eslint-plugin": "5.59.5", 31 | "@typescript-eslint/parser": "5.59.5", 32 | "concurrently": "^8.0.1", 33 | "prettier": "2.8.8", 34 | "tsup": "6.7.0", 35 | "typescript": "5.0.4", 36 | "vitest": "0.31.0" 37 | } 38 | } -------------------------------------------------------------------------------- /packages/config/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist -------------------------------------------------------------------------------- /packages/config/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @css-picons/config 2 | 3 | ## 0.0.5 4 | 5 | ### Patch Changes 6 | 7 | - b599663: Improve deep instantiated return type 8 | - Updated dependencies [b599663] 9 | - @css-picons/types@0.0.5 10 | - @css-picons/variants@0.0.5 11 | 12 | ## 0.0.4 13 | 14 | ### Patch Changes 15 | 16 | - 6892674: - Add transform method for mutating icon svgs. 17 | - Add more ways of loading icons. 18 | - Updated dependencies [6892674] 19 | - @css-picons/variants@0.0.4 20 | - @css-picons/types@0.0.4 21 | 22 | ## 0.0.3 23 | 24 | ### Patch Changes 25 | 26 | - 98f601d: Allow all configurations permitted in recipes by panda css 27 | - Updated dependencies [377f0a2] 28 | - Updated dependencies [98f601d] 29 | - @css-picons/variants@0.0.3 30 | - @css-picons/types@0.0.3 31 | 32 | ## 0.0.2 33 | 34 | ### Patch Changes 35 | 36 | - b613efd: Allow custom collection 37 | - 75e99ce: Bump packages 38 | - Updated dependencies [b613efd] 39 | - Updated dependencies [75e99ce] 40 | - @css-picons/variants@0.0.2 41 | - @css-picons/types@0.0.2 42 | 43 | ## 0.0.1 44 | 45 | ### Patch Changes 46 | 47 | - c9c44db: Initial release 48 | - Updated dependencies [c9c44db] 49 | - @css-picons/types@0.0.1 50 | - @css-picons/variants@0.0.1 51 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@css-picons/config", 3 | "version": "0.0.5", 4 | "license": "MIT", 5 | "description": "Pure CSS icons for css panda", 6 | "main": "dist/index.js", 7 | "module": "dist/index.mjs", 8 | "types": "dist/index.d.ts", 9 | "author": "Abraham - anubra266 ", 10 | "scripts": { 11 | "build": "tsup src/index.ts --format=cjs,esm --shims --dts", 12 | "dev": "pnpm build --watch", 13 | "test": "echo \"Error: no test specified\"" 14 | }, 15 | "files": [ 16 | "dist" 17 | ], 18 | "sideEffects": false, 19 | "publishConfig": { 20 | "access": "public" 21 | }, 22 | "dependencies": { 23 | "@css-picons/types": "workspace:*", 24 | "@css-picons/variants": "workspace:*" 25 | }, 26 | "devDependencies": { 27 | "@pandacss/dev": "dev" 28 | } 29 | } -------------------------------------------------------------------------------- /packages/config/src/index.ts: -------------------------------------------------------------------------------- 1 | import { definePreset } from '@pandacss/dev' 2 | import type { CssPiconsOptions } from '@css-picons/types' 3 | import { getNameVariants } from '@css-picons/variants' 4 | 5 | export const cssPicons = async (options: CssPiconsOptions): Promise => { 6 | const { identifier = 'icon', styles, variants: variantsConfig, ...rest } = options 7 | const nameVariants = await getNameVariants(options) 8 | 9 | const base = Object.assign( 10 | { 11 | width: '1em', 12 | height: '1em', 13 | }, 14 | styles, 15 | ) 16 | 17 | const variants = Object.assign({}, variantsConfig, { 18 | name: nameVariants, 19 | }) 20 | 21 | const recipe = Object.assign( 22 | { 23 | name: 'icon', 24 | description: 'Icon style', 25 | base, 26 | variants, 27 | }, 28 | rest, 29 | ) 30 | 31 | return definePreset({ 32 | theme: { 33 | extend: { 34 | recipes: { 35 | [identifier]: recipe, 36 | }, 37 | }, 38 | }, 39 | }) 40 | } 41 | 42 | export default cssPicons 43 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": false, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./src", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": true, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | "resolveJsonModule": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/types/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist -------------------------------------------------------------------------------- /packages/types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @css-picons/types 2 | 3 | ## 0.0.5 4 | 5 | ### Patch Changes 6 | 7 | - b599663: Improve deep instantiated return type 8 | 9 | ## 0.0.4 10 | 11 | ### Patch Changes 12 | 13 | - 6892674: - Add transform method for mutating icon svgs. 14 | - Add more ways of loading icons. 15 | 16 | ## 0.0.3 17 | 18 | ### Patch Changes 19 | 20 | - 377f0a2: Merge custom collections into `collections` key 21 | - 98f601d: Allow all configurations permitted in recipes by panda css 22 | 23 | ## 0.0.2 24 | 25 | ### Patch Changes 26 | 27 | - b613efd: Allow custom collection 28 | - 75e99ce: Bump packages 29 | 30 | ## 0.0.1 31 | 32 | ### Patch Changes 33 | 34 | - c9c44db: Initial release 35 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@css-picons/types", 3 | "version": "0.0.5", 4 | "license": "MIT", 5 | "description": "Pure CSS icons for css panda", 6 | "main": "dist/index.js", 7 | "module": "dist/index.mjs", 8 | "types": "dist/index.d.ts", 9 | "author": "Abraham - anubra266 ", 10 | "scripts": { 11 | "build": "tsup src/index.ts --format=cjs,esm --shims --dts", 12 | "dev": "pnpm build --watch", 13 | "test": "echo \"Error: no test specified\"" 14 | }, 15 | "files": [ 16 | "dist" 17 | ], 18 | "sideEffects": false, 19 | "publishConfig": { 20 | "access": "public" 21 | }, 22 | "dependencies": { 23 | "@iconify/collections": "^1.0.238", 24 | "@iconify/types": "^2.0.0" 25 | }, 26 | "devDependencies": { 27 | "@pandacss/types": "dev" 28 | } 29 | } -------------------------------------------------------------------------------- /packages/types/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { RecipeConfig, SystemStyleObject } from '@pandacss/types' 2 | import type iconifyCollections from '@iconify/collections/collections.json' 3 | import type { IconifyJSON } from '@iconify/types' 4 | 5 | export { IconifyJSON } 6 | 7 | export type CollectionIcon = { collection: string; icon: string; svg: string | null } 8 | 9 | type IconifyCollections = keyof typeof iconifyCollections 10 | 11 | type CustomCollectionValue = string | (() => string) | (() => Promise) 12 | export type CustomIconRecord = Record | IconifyJSON 13 | 14 | type CustomIconFunctionReturn = IconifyCollections | CustomIconRecord 15 | export type CustomIconFunction = () => CustomIconFunctionReturn | Promise 16 | 17 | export type CustomCollection = IconifyCollections | CustomIconRecord | CustomIconFunction 18 | 19 | export type Collection = IconifyCollections | [string, CustomCollection] 20 | 21 | export interface CssPiconsOptions extends Omit, 'name' | 'description' | 'base'> { 22 | /** 23 | * The jsx elements to track that consumes the exposed recipe. 24 | * 25 | * @default Icon 26 | */ 27 | jsx?: RecipeConfig<{}>['jsx'] 28 | /** 29 | * The identifier for the exposed recipe 30 | * 31 | * @default icon 32 | */ 33 | identifier?: string 34 | collections: Collection[] 35 | /** 36 | * Extra styles applied to the icons by default 37 | * 38 | * @default {} 39 | */ 40 | styles?: SystemStyleObject 41 | /** 42 | * Mode of generated CSS icons. 43 | * 44 | * - `mask` - use background color and the `mask` property for monochrome icons 45 | * - `bg` - use background image for the icons, colors are static 46 | * - `auto` - smartly decide mode between `mask` and `background-img` per icon based on its style 47 | * 48 | * @default 'auto' 49 | * @see https://antfu.me/posts/icons-in-pure-css 50 | */ 51 | mode?: 'mask' | 'bg' | 'auto' 52 | 53 | /** 54 | * 55 | * Transform method lets you transform icons when loading, for example adding fill attribute with currentColor 56 | * @param svg the svg string 57 | * @param collection collection name 58 | * @param icon icon name 59 | * @returns tranformed svg string 60 | * 61 | */ 62 | transform?: (svg: string, collection: string, icon: string) => string 63 | } 64 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": false, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./src", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": true, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | "resolveJsonModule": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/variants/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist -------------------------------------------------------------------------------- /packages/variants/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @css-picons/variants 2 | 3 | ## 0.0.5 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [b599663] 8 | - @css-picons/types@0.0.5 9 | 10 | ## 0.0.4 11 | 12 | ### Patch Changes 13 | 14 | - 6892674: - Add transform method for mutating icon svgs. 15 | - Add more ways of loading icons. 16 | - Updated dependencies [6892674] 17 | - @css-picons/types@0.0.4 18 | 19 | ## 0.0.3 20 | 21 | ### Patch Changes 22 | 23 | - 377f0a2: Merge custom collections into `collections` key 24 | - Updated dependencies [377f0a2] 25 | - Updated dependencies [98f601d] 26 | - @css-picons/types@0.0.3 27 | 28 | ## 0.0.2 29 | 30 | ### Patch Changes 31 | 32 | - b613efd: Allow custom collection 33 | - 75e99ce: Bump packages 34 | - Updated dependencies [b613efd] 35 | - Updated dependencies [75e99ce] 36 | - @css-picons/types@0.0.2 37 | 38 | ## 0.0.1 39 | 40 | ### Patch Changes 41 | 42 | - c9c44db: Initial release 43 | - Updated dependencies [c9c44db] 44 | - @css-picons/types@0.0.1 45 | -------------------------------------------------------------------------------- /packages/variants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@css-picons/variants", 3 | "version": "0.0.5", 4 | "license": "MIT", 5 | "description": "Pure CSS icons for css panda", 6 | "main": "dist/index.js", 7 | "module": "dist/index.mjs", 8 | "types": "dist/index.d.ts", 9 | "author": "Abraham - anubra266 ", 10 | "scripts": { 11 | "build": "tsup src/index.ts --format=cjs,esm --shims --dts", 12 | "dev": "pnpm build --watch", 13 | "test": "echo \"Error: no test specified\"" 14 | }, 15 | "files": [ 16 | "dist" 17 | ], 18 | "sideEffects": false, 19 | "publishConfig": { 20 | "access": "public" 21 | }, 22 | "dependencies": { 23 | "@css-picons/types": "workspace:*", 24 | "@iconify/utils": "^2.1.4" 25 | } 26 | } -------------------------------------------------------------------------------- /packages/variants/src/create-svg.ts: -------------------------------------------------------------------------------- 1 | import { IconifyIcon, iconToSVG, replaceIDs } from '@iconify/utils' 2 | 3 | export function createSvg(iconData: IconifyIcon) { 4 | const svgData = iconToSVG(iconData) 5 | const svgAttributes: Record = { 6 | xmlns: 'http://www.w3.org/2000/svg', 7 | 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 8 | ...svgData.attributes, 9 | } 10 | 11 | const svgAttributesStr = Object.keys(svgAttributes) 12 | .map( 13 | (attr) => 14 | // No need to check attributes for special characters, such as quotes, 15 | // they cannot contain anything that needs escaping. 16 | `${attr}="${svgAttributes[attr as keyof typeof svgAttributes]}"`, 17 | ) 18 | .join(' ') 19 | 20 | const svg = `${replaceIDs(svgData.body)}` 21 | 22 | return svg 23 | } 24 | -------------------------------------------------------------------------------- /packages/variants/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { CssPiconsOptions } from '@css-picons/types' 2 | import { encodeSvgForCss } from '@iconify/utils' 3 | import { resolveIcons } from './resolve-icons' 4 | 5 | export const getNameVariants = async ({ collections, transform, mode = 'auto' }: CssPiconsOptions) => { 6 | const collectionsMap = await Promise.all(collections.map(resolveIcons)) 7 | return collectionsMap.flat().reduce((acc, nxt) => { 8 | if (!nxt.svg) return { ...acc } 9 | const name = `${nxt.collection}:${nxt.icon}` as string 10 | const maskModeName = `${name}?mask` 11 | const bgModeName = `${name}?bg` 12 | 13 | const svg = transform?.(nxt.svg, nxt.collection, nxt.icon) ?? nxt.svg 14 | const { styles, maskStyles, backgroundStyles } = buildVariants(svg, mode) 15 | 16 | return Object.assign(acc, { 17 | [name]: styles, 18 | [maskModeName]: maskStyles, 19 | [bgModeName]: backgroundStyles, 20 | }) 21 | }, {}) 22 | } 23 | 24 | function buildVariants(svg: string, mode: CssPiconsOptions['mode']) { 25 | const uri = `url("data:image/svg+xml;utf8,${encodeSvgForCss(svg)}")` 26 | 27 | const _mode = mode === 'auto' ? (svg.includes('currentColor') ? 'mask' : 'bg') : mode 28 | 29 | const maskStyles = { 30 | mask: `${uri} no-repeat`, 31 | maskSize: '100% 100%', 32 | backgroundColor: 'currentColor', 33 | // for Safari https://github.com/elk-zone/elk/pull/264 34 | color: 'inherit', 35 | } 36 | 37 | const backgroundStyles = { 38 | background: `${uri} no-repeat`, 39 | backgroundSize: '100% 100%', 40 | backgroundColor: 'transparent', 41 | } 42 | 43 | const styles = 44 | _mode === 'mask' 45 | ? // monochrome 46 | maskStyles 47 | : // colored 48 | backgroundStyles 49 | 50 | return { styles, maskStyles, backgroundStyles } 51 | } 52 | -------------------------------------------------------------------------------- /packages/variants/src/resolve-icons/index.ts: -------------------------------------------------------------------------------- 1 | import { Collection, CollectionIcon } from '@css-picons/types' 2 | import { resolveCollectionString } from './resolve-collection-string' 3 | import { resolveCollectionObject } from './resolve-collection-object' 4 | import { resolveCollectionFunction } from './resolve-collection-function' 5 | 6 | export async function resolveIcons(collection: Collection): Promise { 7 | if (typeof collection === 'string') { 8 | return await resolveCollectionString(collection) 9 | } 10 | 11 | const [collectionName, collectionData] = collection 12 | 13 | //* Handle collection strings 14 | if (typeof collectionData === 'string') { 15 | return await resolveCollectionString(collectionData, collectionName) 16 | } 17 | 18 | //* Handle functions 19 | if (isFunction(collectionData)) { 20 | return await resolveCollectionFunction(collectionData, collectionName) 21 | } 22 | 23 | //* Handle objects - iconifyJSON or customiconset 24 | return await resolveCollectionObject(collectionData, collectionName) 25 | } 26 | 27 | const isFunction = (v: any): v is Function => typeof v === 'function' 28 | -------------------------------------------------------------------------------- /packages/variants/src/resolve-icons/resolve-collection-function.ts: -------------------------------------------------------------------------------- 1 | import { CollectionIcon, CustomIconFunction } from '@css-picons/types' 2 | import { resolveCollectionString } from './resolve-collection-string' 3 | import { resolveCollectionObject } from './resolve-collection-object' 4 | 5 | export async function resolveCollectionFunction( 6 | collectionDataFunction: CustomIconFunction, 7 | collectionName: string, 8 | ): Promise { 9 | const collectionData = await collectionDataFunction() 10 | 11 | if (typeof collectionData === 'string') { 12 | return await resolveCollectionString(collectionData, collectionName) 13 | } 14 | return await resolveCollectionObject(collectionData, collectionName) 15 | } 16 | -------------------------------------------------------------------------------- /packages/variants/src/resolve-icons/resolve-collection-object.ts: -------------------------------------------------------------------------------- 1 | import { CollectionIcon, CustomIconRecord, IconifyJSON } from '@css-picons/types' 2 | import { validateIconSet } from '@iconify/utils' 3 | import { resolveIconEntries } from './resolve-icon-entries' 4 | 5 | export async function resolveCollectionObject( 6 | collectionData: CustomIconRecord, 7 | collectionName: string, 8 | ): Promise { 9 | //* Check if it's Iconfiy iconsJSON 10 | if (isIconifyJSON(collectionData)) { 11 | return resolveIconEntries(collectionName, collectionData) 12 | } 13 | 14 | //* When it's a custom icon set 15 | return await Promise.all( 16 | Object.entries(collectionData).map(async ([icon, iconData]) => { 17 | return { 18 | collection: collectionName, 19 | icon, 20 | svg: typeof iconData === 'string' ? iconData : await iconData(), 21 | } 22 | }), 23 | ) 24 | } 25 | 26 | const isIconifyJSON = (v: any): v is IconifyJSON => { 27 | try { 28 | const iconJSON = validateIconSet(v) 29 | return !!iconJSON 30 | } catch (error) { 31 | return false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/variants/src/resolve-icons/resolve-collection-string.ts: -------------------------------------------------------------------------------- 1 | import { CollectionIcon } from '@css-picons/types' 2 | import { resolveIconEntries } from './resolve-icon-entries' 3 | 4 | export async function resolveCollectionString( 5 | collection: string, 6 | collectionName: string = collection, 7 | ): Promise { 8 | try { 9 | const path = `@iconify-json/${collection}/icons.json` 10 | 11 | return resolveIconPath(collectionName, path) 12 | } catch (error) { 13 | try { 14 | const path = `@iconify/json/json/${collection}.json` 15 | 16 | return resolveIconPath(collectionName, path) 17 | } catch (error) { 18 | console.log( 19 | '\x1b[31m', 20 | `You provided ${collection} as an icon collection in your panda config. You must install the ${`@iconify-json/${collection}`} package or to get all icons; install the @iconify/json package`, 21 | ) 22 | process.exit(1) 23 | } 24 | } 25 | } 26 | 27 | function resolveIconPath(collection: string, path: string) { 28 | const iconsJson = require(path) 29 | return resolveIconEntries(collection, iconsJson) 30 | } 31 | -------------------------------------------------------------------------------- /packages/variants/src/resolve-icons/resolve-icon-entries.ts: -------------------------------------------------------------------------------- 1 | import { CollectionIcon, IconifyJSON } from '@css-picons/types' 2 | import { getIconData } from '@iconify/utils' 3 | import { createSvg } from '../create-svg' 4 | 5 | export function resolveIconEntries(collectionName: string, iconObj: IconifyJSON): CollectionIcon[] { 6 | const collectionIconEntries = Object.keys(iconObj.icons) 7 | return collectionIconEntries.map((icon) => { 8 | const iconData = getIconData(iconObj, icon) 9 | const svg = iconData ? createSvg(iconData) : null 10 | return { collection: collectionName, icon, svg } 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /packages/variants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": false, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./src", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": true, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | "resolveJsonModule": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | # pnpm-workspace.yaml 2 | packages: 3 | - 'packages/*' 4 | - 'site' 5 | -------------------------------------------------------------------------------- /site/.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 | 26 | ## Panda 27 | styled-system 28 | -------------------------------------------------------------------------------- /site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CSS Picons Demo 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sandbox-vite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "prepare": "panda", 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "preview": "vite preview", 11 | "css": "PANDA_DEBUG=ast:* panda", 12 | "css:gen": "PANDA_DEBUG=* panda codegen", 13 | "css:dev": "PANDA_DEBUG=file:* panda --watch" 14 | }, 15 | "dependencies": { 16 | "@iconify-json/carbon": "^1.1.16", 17 | "@iconify-json/fe": "^1.1.6", 18 | "@iconify-json/ph": "^1.1.5", 19 | "node-fetch": "^3.3.1", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0" 22 | }, 23 | "devDependencies": { 24 | "@css-picons/config": "workspace:*", 25 | "@iconify-json/cryptocurrency-color": "^1.1.3", 26 | "@iconify-json/mdi": "^1.1.52", 27 | "@pandacss/dev": "dev", 28 | "@types/react": "18.2.6", 29 | "@types/react-dom": "18.2.4", 30 | "@vitejs/plugin-react": "4.0.0", 31 | "postcss": "8.4.23", 32 | "typescript": "5.0.4", 33 | "vite": "4.3.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /site/panda.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@pandacss/dev' 2 | import { cssPicons } from '@css-picons/config' 3 | import feIconsData from '@iconify-json/fe/icons.json' 4 | import phIconsData from '@iconify-json/ph/icons.json' 5 | import fs from 'node:fs' 6 | import fetch from 'node-fetch' 7 | 8 | const iconPreset = cssPicons({ 9 | collections: [ 10 | 'mdi', 11 | 'cryptocurrency-color', 12 | [ 13 | 'custom', 14 | { 15 | circle: '', 16 | vite: () => fs.readFileSync('./public/vite.svg', { encoding: 'utf-8' }), 17 | }, 18 | ], 19 | [ 20 | 'local', 21 | //* Load all svgs in a directory 22 | () => { 23 | const files = fs.readdirSync('./public') 24 | const svgFiles = files.filter((n) => n.endsWith('.svg')) 25 | if (!svgFiles.length) console.error('No SVG files in path') 26 | const iconSet = svgFiles.reduce( 27 | async (acc, nxt) => 28 | Object.assign(acc, { [nxt.split('.svg')[0]]: fs.readFileSync(`./public/${nxt}`, { encoding: 'utf-8' }) }), 29 | {}, 30 | ) 31 | return iconSet 32 | }, 33 | ], 34 | [ 35 | 'solar', 36 | { 37 | 'airbuds-outline': async () => { 38 | //* fetch icon svg from a remote server: 39 | // ! We use node-fetch package because we can't access the native fetch 40 | return await fetch('https://api.iconify.design/solar:airbuds-outline.svg?color=%23888888').then((res) => 41 | res.text(), 42 | ) 43 | }, 44 | }, 45 | ], 46 | ['fe', feIconsData], 47 | ['carbon', () => import('@iconify-json/carbon/icons.json').then((i) => i.default as any)], 48 | ['ph', () => phIconsData], 49 | [ 50 | 'circle-flags', 51 | async () => { 52 | //* fetch iconifyJson from a remote server: 53 | //! We use node-fetch package because we can't access the native fetch 54 | return await fetch('https://raw.githubusercontent.com/iconify/icon-sets/master/json/circle-flags.json').then( 55 | (res) => res.json(), 56 | ) 57 | }, 58 | ], 59 | ], 60 | 61 | transform(svg, collection, icon) { 62 | // ! Put a working sample 63 | // do not apply fill to this icons on this collection 64 | if (collection === 'circle-flags' && icon === 'ng') return svg 65 | return svg.replace(/#fff/, 'currentColor') 66 | }, 67 | 68 | styles: { 69 | verticalAlign: 'middle', 70 | display: 'inline-block', 71 | }, 72 | }) 73 | 74 | export default defineConfig({ 75 | presets: ['@pandacss/dev/presets', iconPreset], 76 | preflight: true, 77 | include: ['./src/**/*.{tsx,ts}'], 78 | outdir: 'styled-system', 79 | jsxFramework: 'react', 80 | }) 81 | -------------------------------------------------------------------------------- /site/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | '@pandacss/dev/postcss': {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /site/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { css, cx } from '../styled-system/css' 2 | import { flex, stack } from '../styled-system/patterns' 3 | import { icon, IconVariantProps } from '../styled-system/recipes' 4 | import { SystemStyleObject } from '../styled-system/types' 5 | 6 | type IconProps = IconVariantProps & SystemStyleObject 7 | function Icon({ name, ...rest }: IconProps) { 8 | return ( 9 | 18 | ) 19 | } 20 | 21 | function App() { 22 | return ( 23 |
24 | mdi:account-alert-outline 25 | 26 | 27 | mdi:alpha-e (color: red.400) 28 | 29 | cryptocurrency-color:aave 30 | 31 | 32 | custom:circle 33 | 34 | custom:vite 35 | 36 | loal:vite 37 | 38 | solar:airbuds-outline 39 | 40 | fe:check-verified 41 | 42 | carbon:build-tool 43 | 44 | ph:alien 45 | 46 | circle-flags:ng 47 | 48 |
49 | ) 50 | } 51 | 52 | export default App 53 | -------------------------------------------------------------------------------- /site/src/index.css: -------------------------------------------------------------------------------- 1 | @layer reset, base, tokens, recipes, utilities; 2 | -------------------------------------------------------------------------------- /site/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /site/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /site/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /site/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | base: './', 7 | plugins: [react()], 8 | build: { 9 | outDir: 'dist_site', 10 | }, 11 | }) 12 | --------------------------------------------------------------------------------