{"id":26531600,"url":"https://github.com/kushidesign/kushi","last_synced_at":"2025-04-05T08:04:54.267Z","repository":{"id":41338219,"uuid":"393599943","full_name":"kushidesign/kushi","owner":"kushidesign","description":"UI design library for ClojureScript","archived":false,"fork":false,"pushed_at":"2025-03-27T16:48:02.000Z","size":7566,"stargazers_count":84,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-01T17:28:02.054Z","etag":null,"topics":["clojure","clojurescript","css","design","design-system","ui","ui-components","ui-design"],"latest_commit_sha":null,"homepage":"http://kushi.design/","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kushidesign.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-07T06:26:55.000Z","updated_at":"2025-03-20T14:06:15.000Z","dependencies_parsed_at":"2025-01-18T17:35:01.702Z","dependency_job_id":"01a7b049-1f98-432f-8cd3-d3b82b163720","html_url":"https://github.com/kushidesign/kushi","commit_stats":{"total_commits":671,"total_committers":1,"mean_commits":671.0,"dds":0.0,"last_synced_commit":"2cf9da83738eda5b0a8cdb9cd5fe6f54da57ef46"},"previous_names":["paintparty/kushi"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kushidesign%2Fkushi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kushidesign%2Fkushi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kushidesign%2Fkushi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kushidesign%2Fkushi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kushidesign","download_url":"https://codeload.github.com/kushidesign/kushi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247305932,"owners_count":20917208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","css","design","design-system","ui","ui-components","ui-design"],"created_at":"2025-03-21T18:22:42.481Z","updated_at":"2025-04-05T08:04:54.240Z","avatar_url":"https://github.com/kushidesign.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp\u003e\u003csub\u003eMany thanks to \u003ca href=\"https://www.clojuriststogether.org/\"\u003eClojurists Together\u003c/a\u003e for generously supporting this project!\u003c/sub\u003e\u003c/p\u003e\n\n\u003cbr\u003e\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"./docs/public/graphics/images/kushi-ui-iso-1.png\" width=\"850\"\u003e\u003c/img\u003e\u003c/div\u003e \n\u003cbr\u003e\n\n# Kushi\n\u003ch5\u003eKushi is a foundation for building web UI with ClojureScript.\u003c/h5\u003e\n\u003ch5\u003e\u003ca href=\"https://kushi.design\" target=\"_blank\"\u003e\u003cstrong\u003eExplore Kushi UI Playground »\u003c/strong\u003e\u003c/a\u003e\u003c/h5\u003e\n\u003ch5\u003e\u003ca href=\"https://github.com/kushidesign/kushi-quickstart\" target=\"_blank\"\u003e\u003cstrong\u003eKushi Quickstart »\u003c/strong\u003e\u003c/a\u003e\u003c/h5\u003e\n\n\n\n\u003cbr\u003e\n\n## Features\n- **100% Clojure(Script)**\n\n- **Suite of accessible,  headless UI components**\n\n- **Themeable design system foundation**\n\n- **Co-location of styling at the element level**\n\n- **Shorthand styling syntax shadows CSS standard**\n\n- **Supports media-queries, psuedos, and combo selectors**\n\n- **Leverages CSS variables for runtime dynamics**\n\n- **Composable, user-defined shared classes**\n\n- **Many useful CSS utility classes**\n\n- **Default industry-standard breakpoint scale**\n\n- **Auto-generated selectors to avoid pontential collisions**\n\n- **Flexible selector prefixing options**\n\n- **Helpers for typography, keyframe animations, and more**\n\n- **Enhanced debugging via metadata**\n\n- **Detailed, human-readable warnings**\n\n- **Framework \u0026 build-tool agnostic**\n\n- **Generates interactive UI documentation**\n\n\u003cbr\u003e\n\n\u003e [!WARNING]\n\u003e\n\u003e If your project is using Kushi `v1.0.0-a.22` or lower, please reference the README on a different branch that corresponds with the version that you are using:\u003cbr\u003e\n\u003e [v1.0.0-a.22](https://github.com/kushidesign/kushi/tree/refactor-warnings-and-errors)\u003cbr\u003e\n\u003e [v1.0.0-a.21](https://github.com/kushidesign/kushi/tree/playground-update2)\u003cbr\u003e\n\u003e [v1.0.0-a.20](https://github.com/kushidesign/kushi/tree/tooltip2)\u003cbr\u003e\n\u003e [v1.0.0-a.19](https://github.com/kushidesign/kushi/tree/cssvar-tuples-defclass)\u003cbr\u003e\n\u003e [v1.0.0-a.18](https://github.com/kushidesign/kushi/tree/alpha18-fixes)\u003cbr\u003e\n \n\n\n\u003cbr\u003e\n\n## Table of Contents\n- [Introduction](#introduction)\u003cbr\u003e\n- [Project Status](#project-status)\u003cbr\u003e\n- [Setup and usage](#setup-and-usage)\u003cbr\u003e\n- [Kushi styling syntax](#kushi-styling-syntax)\u003cbr\u003e\n- [Shared styles](#shared-styles)\u003cbr\u003e\n- [Styles as tokenized keywords](#styles-as-tokenized-keywords)\u003cbr\u003e\n- [Working with media queries](#working-with-media-queries)\u003cbr\u003e\n- [Pseudos and combo selectors](#pseudos-and-combo-selectors)\u003cbr\u003e\n- [Selector prefixing options](#selector-prefixing-options)\u003cbr\u003e\n- [Defining CSS at-rules](#defining-css-at-rules)\u003cbr\u003e\n- [Injecting stylesheets](#injecting-stylesheets)\u003cbr\u003e\n- [Configuration options](#configuration-options)\u003cbr\u003e\n- [Actionable warnings](#actionable-warnings)\u003cbr\u003e\n- [Defining components](#defining-components)\u003cbr\u003e\n- [Kushi Playground](#kushi-playground)\n- [Usage with build tools](#usage-with-build-tools)\u003cbr\u003e\n- [Contributing](#contributing)\u003cbr\u003e\n- [License](#license)\n\n\u003cbr\u003e\n\n## Introduction\nKushi provides a comprehensive solution for creating and evolving web-based UI\nprojects in ClojureScript.\n\nThe following features work in concert, making it easy to roll your own design\nsystem:\n- A set of professionally designed, themeable, headless UI components\n- Solid foundation of hand-tuned global design tokens\n- Functional styling engine\n- Configurable theming\n\nUsage of Kushi's design system and component library is completly optional. You\ncan just use the styling engine as a pure ClojureScript alternative to\nmainstream JS solutions such as Tailwind, Emotion, etc.\n\n\u003cbr\u003e\n\n## Project status\nCurrent version is pre-release intended for early adopters and anyone who would\nlike to provide feedback. New 1.0 alphas will be released frequently, while I\ncontinue to make improvements/changes/additions. Working towards a stable 1.0\nrelease by end of Q2 2025.\n\n\nPlease report anything unexpected on GitHub Issues.\n\n\u003cbr\u003e\n\n## Setup and Usage\n[![Clojars Project](https://img.shields.io/clojars/v/design.kushi/kushi.svg)](https://clojars.org/design.kushi/kushi)\n\nUsage with [Reagent](https://reagent-project.github.io/) +\n[Shadow-CLJS](https://github.com/thheller/shadow-cljs) is currently recommended.\n\nPlease check out\n[Kushi Quickstart](https://github.com/kushidesign/kushi-quickstart) for a well\ncommented, feature-complete minimal project template. This is probably the\neasiest way to get started with Kushi.\n\nCheckout the\n\u003ca href=\"https://kushidesign.github.io/kushi/public/index.html\" target=\"_blank\"\u003e\ninteractive playground\u003c/a\u003e of pre-built headless UI components.\n\u003cbr\u003e\n\n\n\u003c!--Intro section for ui lib vs user guide --\u003e\n\n## Build system basics\nCurrently, Kushi depends on the [`shadow-cljs`](https://github.com/thheller/shadow-css)\nbuild-hook system to generate and bundle CSS. The `sx` and `css` macros return\nan html attributes map or class string, respectively. The CSS is transpiled and\ngenerated in a separate analyzation phase triggered by `kushi.css.build.analyze/hook`.\nBy default, [`lightningcss`](https://lightningcss.dev/) is leveraged to achieve\nfast and efficient minification, bundling, vendor prefixing, and syntax-lowering\n(to target older browsers).\n\n\u003cbr\u003e\n\n## Kushi styling syntax\n\n### Basic usage of the `css` macro\n\nStyles are co-located at the element level. You don't need to think about\nchoosing an appropriate classname, as it is generated automatically. The macro\n`kushi.core/css` takes any number of styles:\n\n```Clojure\n(ns myns.core\n  (:require\n   [kushi.core :refer [css]]))\n\n(defn my-component []\n [:div\n  {:class (css :c--red\n               :ta--c\n               :fs--18px)}])\n\n```\n\nAs you can see in the above example, Kushi promotes a simple\ntokenized-keyword-based shorthand grammar which shadows standard CSS. This\napproach is similar solutions such as Tachyons and Tailwind, but much more\nhelpful in learning actual CSS, and much more intuitive if you are an existing\nCSS expert.\n\n\nIn the example above, the `css` macro would expand to the following (shown in\ncontext):\n\n```Clojure\n(defn my-component []\n [:div\n  {:class \"myns_core__L7C11\"}])\n```\n\nWhen your build finishes, the following css will be written to disk:\n\n```css\n.myns_core__L7C11 {\n  color: red;\n  text-align: center;\n  font-size: 18px;\n}\n```\nCheck out the [Styles as tokenized keywords](#styles-as-tokenized-keywords)\nsection for more details on Kushi's shorthand grammar.\n\nNote that the shorthand grammar is totally optional - you can also write these\ntokenized keywords with fully hydrated props and values.\n```Clojure\n(defn my-component []\n [:div\n  {:class (css :color--red\n               :text-align--center\n               :font-size--18px)}])\n```\n\nIf you have an aversion to the tokenized keyword approach, you can also just use\na map - check out the [Using maps](#using-maps) section. \n\n\u003cbr\u003e\n\n### Supplying additional classes to the `css` macro\n\nYou can supply additional classes as needed. These classes might be shared\nclasses that you have defined, utility classes that ship with Kushi, or classes\nfrom 3rd party libraries. They must take the form of a keyword prefixed with a\ndot: \n\n```Clojure\n(defn my-component []\n [:div\n  {:class (css :.absolute-centered\n               :.text-large\n               :c--red\n               :ta--c\n               :fs--18px)])\n```\nThe above call to `css` would expand to the following class string:\n```Clojure\n\"myns_core__L7C11 absolute-centered text-large\"\n```\n\n\u003cbr\u003e\n\n### Supplying dynamic classes to the `css` macro\n\nIf you want to supply classes conditionally, based on runtime logic, you can do\nso like this:\n\n```Clojure\n(defn my-component [positioning-class]\n [:div\n  {:class (css positioning-class\n               :c--red\n               :ta--c\n               :fs--18px)])\n\n;; At call site\n[my-component \"absolute-centered\"]\n```\n\nThe above call to `css` would expand to the following:\n```Clojure\n(str \"myns_core__L7C11 \" positioning-class)\n\n;; At runtime, based on the example call-site value, this would resolve to:\n\"myns_core__L7C11 absolute-centered\"\" \n```\n\n\u003c!-- Another example of supplying classes conditionally:\n\n```Clojure\n(defn my-component [font-size-class]\n (let [a (when (= font-size-class \"text-xlarge\")\n           \"absolute\")]\n   [:div\n    {:class (css a\n                 font-size-class\n                 :c--red\n                 :ta--c\n                 :fs--18px)])\n\n;; At call site\n[my-component \"text-xlarge\"]\n```\nThe above call to `css` would expand to the following:\n```Clojure\n(str \"myns_core__L7C11 \" a \" \" font-size-class)\n\n;; At runtime, based on the example call-site value, this would resolve to:\n\"myns_core__L7C11 absolute text-xsmall\"\n``` --\u003e\n\nYou can also define more than one class using `css` and apply one conditionally:\n\n```Clojure\n(defn my-component [k]\n (let [foo-class (css :c--red :bgc--black :fs--48px)\n       bar-class (css :c--blue :bgc--gray :fs--28px)\n       baz-class (css :c--orange :bgc--beige :fs--18px)\n       my-class (case k\n                  :foo foo-class\n                  :bar bar-class\n                  :baz baz-class\n                  nil)]\n   [:div\n    {:class my-class}])\n\n;; At call site\n[my-component :foo]\n```\n\n\u003cbr\u003e\n\n### Using css custom properties (aka css variables) with the `css` macro.\nIn the tradition of Sass and Less, Kushi uses a leading `$` syntax for css\ncustom properties\n\nThe example below uses `:c--$red-500`, which will set the `color` property to\n`var(--red-500)`. In this case, `var(--red-500)` is a global variable that is\npredefined within the design token system that ships with Kushi.\n\n```Clojure\n(defn my-component []\n [:div\n  {:class (css :.absolute\n               font-size-class\n               :c--$red-500\n               :ta--c\n               :fs--18px)])\n```\n\u003cbr\u003e\n\n### Supplying dynamic values for individual css properties with the `css` macro.\nIf you want to supply dynamic values for individual css properties, you can\nutilize the `kushi.core/css-vars` macro, or the `kushi.core/css-vars-map` macro\n(if you are using React under the hood). This will create a \"local\" custom css\nproperty in the `style` attribute that you will then reference within your call\nto `css` using the `$` css variable syntax:\n\n```Clojure\n(defn my-component [text-color]\n [:div\n  {:style (css-vars text-color)\n   :class (css :.absolute\n               font-size-class\n               :c--$text-color\n               :ta--c\n               :fs--18px)])\n\n;; At call site\n[my-component \"red\"]\n```\n\nIn the example above, the `css` macro, and the `css-vars` macro would expand to\nthe following (shown in context):\n```Clojure\n(defn my-component [text-color]\n [:div\n  {:style (str \"--text-color: \" text-color)\n   :class \"myns_core__L7C11\"}])\n```\n\nWhen your build finishes, the following css will be written to disk:\n\n```css\n.myns_core__L7C11 {\n  color: var(--text-color);\n  text-align: center;\n  font-size: 18px;\n}\n```\n\nIf you are using Kushi with a React abstraction such as reagent, you will\nprobably want use the `kushi.core/css-vars-map` macro instead, which would\nexpand to this:\n\n```Clojure\n(defn my-component [text-color]\n [:div\n  {:style {\"--text-color\" text-color}\n   :class \"myns_core__L7C11\"}])\n```\n\n\u003cbr\u003e\n\n### Reducing ceremony with the `sx` macro\nIf you don't need to use dynamic values as in the example above, and you don't\nneed to supply html attributes other than `class`, you can use use the\n`kushi.core/sx` macro to style elements and reduce some of the boilerplate.\nIt works the same as the `css` macro, but returns a map with a `:class` entry\ninstead of a string:\n\n```Clojure\n(ns myns.core\n  (:require\n   [kushi.core :refer [css sx]]))\n\n(defn my-component []\n [:div\n  (sx :c--red\n      :ta--c\n      :fs--18px)}])\n```\n\nWhich would expand to the following (shown in context):\n\n```Clojure\n(defn my-component []\n [:div\n  {:class \"myns_core__L7C11\"}])\n```\n\n\n\u003cbr\u003e\n\n### Modifier syntax: pseudo-classes \nKushi offers a modifier syntax for conveniently describing things like\npseudo-classes:\n\n```Clojure\n(css :c--red\n     :hover:c--blue\n     :hover:td--u)\n```\n\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  color: red;\n  \u0026:hover {\n    color: blue;\n    text-decoration: underline;\n  }\n}\n```\n\n\u003cbr\u003e\n\n### Modifier syntax: nested selectors\nYou can use the same modifier syntax for nested selectors. Underscore chars `_`\nare transformed to a spaces:\n\n```Clojure\n(css :c--red\n     :\u003ep:c--teal\n     :\u003ep.foo:c--orange\n     :_a:c--purple)\n```\n\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  color: red;\n  \u0026\u003ep {\n    color: teal;\n    \u0026.foo {\n      color: orange;\n    }\n  }\n  \u0026 a {\n    color: purple;\n  }\n}\n```\n\n\u003cbr\u003e\n\n### Modifier syntax: breakpoints \n\nYou can use the same modifier syntax for media queries:\n\n```Clojure\n(css :fs--18px\n     :lg:fs--22px)\n```\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  font-size: 18px;\n  @media (min-width: 1024px) {\n    font-size: 22px;\n  }\n}\n```\nSee the [Working with media queries](#working-with-media-queries) section for\nmore details on media queries and Kushi's default breakpoint scale.\n\n\u003cbr\u003e\n\n### Modifier syntax: dark-mode \nUsing the modifier syntax for dark-mode styling:\n\n```Clojure\n(css :c--black\n     :dark:c--white)\n```\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  color: black; \n  .dark \u0026 {\n    color: white;\n  }\n}\n```\nSee the [Working with dark-mode](#working-with-dark-mode) section for more\ndetails on media queries and Kushi's built-in functionality for dark-mode.\n\n\u003cbr\u003e\n\n### Modifier syntax: stacking\n\nThese modifiers are designed to be \"stacked\". They must be separated with a\ncolon and the order must be media-query (optional), dark-mode (optional), then\n any sequence of selectors and pseudo-class/pseudo-elements:\n\n```Clojure\n(css :c--black\n     :hover:c--red\n     :lg:hover:c--orange\n     :dark:c--white\n     :dark:hover:c--hotpink\n     :lg:dark:hover:c--yellow\n     :lg:dark:hover:\u003ediv.foo:c--silver)\n```\nThe above example produces the following css:\n```css\n\n.myns_core__L7C11 {\n  color: black;\n  @media(min-width: 1024px) {\n    .dark \u0026 {\n      \u0026:hover {\n        color: yellow;\n        \u0026\u003ediv.foo {\n          color: silver;\n        }\n      }\n    }\n    \u0026:hover {\n      color: orange;\n    }\n  }\n  .dark \u0026 {\n    color: white;\n    \u0026:hover {\n      color: hotpink;\n    }\n  }\n  \u0026:hover {\n    color: red;\n  }\n}\n```\n\n\u003cbr\u003e\n\n### Using maps \nIf Kushi's tokenized keyword syntax isn't your speed, your can also just use\nmaps to describe your all your styles:\n\n```Clojure\n(css {:color      :red\n      :text-align :center\n      :font-size  :18px})\n```\n\n\nYou can also mix in maps with tokenized keywords. Maps are very useful when you\nwant to use nesting to avoid repetition:\n\n```Clojure\n(css :c--red\n     {:hover {:c   :blue\n              :td  :underline\n              :bgc :yellow}})\n```\n\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  color: red;\n  \u0026:hover {\n    color: blue;\n    text-decoration: underline;\n    background-color: yellow;\n  }\n}\n```\n\nYou can nest as deep as you want:\n```Clojure\n(css :c--red\n     {:\u003ep {:hover {:c   :blue\n                   :td  :underline\n                   :bgc :yellow\n                   :_a  {:c   :purple\n                         :td  :none\n                         :bgc :pink}}})\n```\n\nThe above example produces the following css:\n```css\n.myns_core__L7C11 {\n  color: red;\n  \u0026\u003ep {\n    \u0026:hover {\n      color: blue;\n      text-decoration: underline;\n      background-color: yellow;\n      \u0026 a {\n        color: purple;\n        text-decoration: none;\n        background-color: pink;\n      }\n    }\n  }\n}\n```\n\n\u003cbr\u003e\n\nThe following is a more complex example taken from a working codebase.\nThere is a single map entry that defines styles for some direct descendant\nelements in a sidenav. The styles only apply, however, if the element targeted\nby the selector has an ancestor that matches\n`\"nav[data-foo-bar-sidenav][aria-expanded=\\\"true\\\"]`.\nThis works because of the appended `\u0026` character:\n\n```Clojure\n(css {\"nav[data-foo-bar-sidenav][aria-expanded=\\\"true\\\"] \u0026\"\n      {:\u003e.sidenav-menu-icon:d  :none\n       :\u003e.sidenav-close-icon:d :inline-flex\n       :\u003eul:h                  \"calc((100vh - (var(--navbar-height) * 2)) * 1)\"\n       :o                      1}}\n```\n\nThe above example produces the following css:\n\n```css\n.myns_core__L7C11 {\n  nav[data-foo-bar-sidenav][aria-expanded=\"true\"] \u0026 {\n    \u0026\u003e.sidenav-menu-icon {\n      display: none;\n    }\n    \u0026\u003e.sidenav-close-icon {\n      display: inline-flex;\n    }\n    \u0026\u003eul {\n      height: calc((100vh - (var(--navbar-height) * 2)) * 1);\n    }\n    opacity: 1;\n  }\n}\n```\n\n\u003cbr\u003e\n\n\u003c!-- ### Using vectors\nYou may want or need to express a style as a 2-element vector.\n\nWhen a string is desired, or necessary:\n```Clojure\n(sx [:before:content \"\\\"$\\\"\"]\n    [:width \"calc((100vw / 3) + 12px)\"])\n```\nWhen constructing a value using css function syntax:\n```Clojure\n(sx [:transform '(translateY :-100px)]])\n``` --\u003e\n\n### CSS Shorthand Properties\n[CSS shorthand properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties)\nare a fundamental feature of CSS. They are properties that let you set the\nvalues of multiple other CSS properties simultaneously. With Kushi, you can\nwrite them like this:\n\n```Clojure\n;; with tokenized keyword\n(css :b--1px:solid:black)\n\n;; if using a map\n(css {:b :1px:solid:black})\n\n;; same as above, with string\n(css {:b \"1px solid black\"})\n\n\n```\n\nAll of the above examples will resolve to the following css declaration:\n```css\nborder: 1px solid black;\n```\n\u003cbr\u003e\n\n### CSS Value Lists\nIn css, sometimes multiple values are seperated by commas to indicate they are\nordered, or that there are ordered alternatives. With Kushi, you can write them\nlike this:\n```Clojure\n(css :ff--FiraCodeRegular|Consolas|monospace)\n```\nThe above will resolve to the following css declaration:\n```css\nfont-family: FiraCodeRegular, Consolas, monospace;\n```\nThe example below uses a list of css shorthand values in order to render\nmultiple text-shadows in different colors:\n```Clojure\n(css :text-shadow--5px:5px:10px:red|-5px:-5px:10px:blue)\n```\nThe above will resolve to the following css declaration:\n```css\ntext-shadow: 5px 5px 10px red, -5px -5px 10px blue;\n```\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n## Shared Styles\n`kushi.core/defcss` is intended for the creation of shared styles.\n\nThese shared styles should be defined in a dedicated namespace, or set of\ndedicated namespaces, and required once in your core or main ns.\n\n`defcss` takes a selector (string) as the first argument, followed by any number\nof style arguments. Any style argument that is valid for `css` macro is valid\nfor `defcss`.\n\n```Clojure\n(ns myapp.shared-styles\n  (:require\n   [kushi.core :refer [defcss]]))\n\n\n;; Using tokenized keywords\n(defcss \".headline\"\n  :ta--left\n  :w--100%\n  :ff--Inter|system-ui|sans-serif\n  :fw--900\n  :fs--24px\n  :tt--u\n  :mix-blend-mode--darken)\n\n\n;; Tokenized-keywords + usage of a map for css function syntax\n(defcss \".headline2\"\n  :top--0\n  :left--0\n  :b--1px:solid:black\n  :fs--200px\n  :tt--u\n  :mix-blend-mode--darken\n  {:c \"rgba(155 155 155 / 0.8)\"})\n\n\n;; Example using a single map.\n(defcss \".headline3\"\n  {:top               0\n   :left              0\n   :b                 :1px:solid:black\n   :fs                :200px\n   :tt                :u\n   :mix-blend-mode    :darken\n   :c                 \"rgba(155 155 155 / 0.8)\"})\n```\n\nBy authoring your shared styles in a dedicated ns (or namespaces), you only need\nto require once in your main or core ns, and all the styles from that ns will be\navailable globally.\n```Clojure\n(ns myapp.core\n  (:require\n   [kushi.core :refer [sx]]\n   [myapp.shared-styles]))\n\n  (defn my-headline [text]\n    [:h1 (sx :.headline :mt--5px) text])\n```\n\n\n\u003c!-- TODO Update this, making sure it is accurate with latest version --\u003e\n\n\u003c!-- With `defclass`, you can mix-in any other defined classes:\u003cbr\u003e\n```Clojure\n(defclass headline\n  :.flex-row-fs\n  :top--0\n  :left--0\n  :b--1px:solid:black\n  :fs--200px\n  :tt--u\n  :fs--italic\n  :mix-blend-mode--darken)\n```\n\n\n(defclass headline-colored\n  :.headline\n  :c--red\n  :b--1px:solid:pink)\n```\n\nIn the example above, the `:.headline` class is one of several predefined\nclasses that ships with kushi. --\u003e\n\n\n\u003c!-- TODO Update this --\u003e\n\u003c!-- \u003cbr\u003e\n\n### Kushi's predefined utility classes:\n\n```Clojure\n;; Positioning\n\n:.absolute\n:.absolute-centered\n:.absolute-fill\n:.relative\n:.fixed\n:.fixed-fill\n\n\n;; Background-images\n\n:.bgi-contain\n:.bgi-cover\n:.debug-grid\n:.debug-grid-16\n:.debug-grid-16-solid\n:.debug-grid-8-solid\n\n\n;; Flex layouts\n\n:.flex-col-c\n:.flex-col-fe\n:.flex-col-fs\n:.flex-col-sa\n:.flex-col-sb\n:.flex-col-se\n:.flex-row-c\n:.flex-row-fe\n:.flex-row-fs\n:.flex-row-sa\n:.flex-row-sb\n:.flex-row-se\n\n\n;; Borders \u0026 outlines\n\n:.bordered\n:.outlined\n:.pill\n\n\n;; Type styling\n\n:.sans\n:.italic\n:.oblique\n:.uppercase\n:.lowercase\n:.capitalize\n:.full-width\n:.full-width-kana\n\n\n;; Type weight\n\n:.thin\n:.extra-light\n:.light\n:.regular\n:.medium\n:.semi-bold\n:.bold\n:.extra-bold\n:.heavy\n\n\n;; Cursor\n\n:.pointer\n\n\n;; Transitions\n\n:.transition\n\n\n``` --\u003e\n\u003c!-- TODO add debug grid helpers to above list --\u003e\n\n\n\u003c!-- TODO update this with latest file path --\u003e\n\u003c!-- Checkout\n\u003ca href=\"https://github.com/kushidesign/kushi/blob/main/src/kushi/ui/utility.cljc\"\n   target=\"_blank\"\u003ethis source file\u003c/a\u003e\n for a complete reference of all current pre-defined utility classes.\n\u003cbr\u003e --\u003e\n\n\u003cbr\u003e\n\n## Styles as tokenized keywords\nWith the `css`, `sx`, and `defcss` macros, the simplest and most convenient way\nto describe styles is the usage of tokenized keywords. These keywords contain a\n`--`, and represent a css prop and value pair (split on `--`).\n\n\n```Clojure\n:color--red\n```\n\nMore examples, using Kushi's optional shorthand grammer.\n```Clojure\n:c--red    ; :color--red\n:ai--c     ; :align-items--center\n:ai--e     ; :align-items--end\n:ta--r     ; :text-align--right\n:fs--18px  ; :font-size--18px\n:ff--serif ; :font-family--serif\n```\nThis shorthand grammer is available for the most commonly used props:\n```Clojure\n:ai   ; :align-items\n:b    ; :border\n:bc   ; :border-color\n:bi   ; :border-inline\n:bb   ; :border-block\n:bs   ; :border-style\n:bw   ; :border-width\n:bg   ; :background\n:c    ; :color\n:d    ; :display\n:ff   ; :font-family\n:fs   ; :font-size\n:fv   ; :font-variant\n:fw   ; :font-weight\n:h    ; :height\n:jc   ; :justify-content\n:ji   ; :justify-items\n:lh   ; :line-height\n:m    ; :margin\n:mb   ; :margin-block\n:mbs  ; :margin-block-start\n:mbe  ; :margin-block-end\n:mi   ; :margin-inline\n:mis  ; :margin-inline-start\n:mie  ; :margin-inline-end\n:o    ; :opacity\n:p    ; :padding\n:pb   ; :padding-block\n:pbs  ; :padding-block-start\n:pbe  ; :padding-block-end\n:pi   ; :padding-inline\n:pis  ; :padding-inline-start\n:pie  ; :padding-inline-end\n:ta   ; :text-align\n:td   ; :text-decoration\n:tt   ; :text-transform\n:w    ; :width\n:ws   ; :white-space\n:zi   ; :z-index\n```\n\n\u003c!-- TODO maybe just link to source? --\u003e\nSee the complete list of supported css properties\n\u003ca href=\"https://github.com/kushidesign/kushi/blob/main/docs/kushi-shorthand-reference.md\"\ntarget=\"_blank\"\u003ehere\u003c/a\u003e.\n\nShorthand grammer extends to cover enumerated values:\n```Clojure\n;; text-decoration\n:td--u   ; text-decoration--uppercase\n:td--o   ; text-decoration--overline\n:td--lt  ; text-decoration--line-through\n\n;; background-repeat\n:bgr--nr ; background-repeat--no-repeat\n:bgr--rx ; background-repeat--repeat-x\n:bgr--ry ; background-repeat--repeat-y\n:bgr--r  ; background-repeat--round\n:bgr--s  ; background-repeat--space\n\n;; align-items\n:ai--c   ; align-items--center\n:ai--fs  ; align-items--flex-start\n:ai--fe  ; align-items--flex-end\n:ai--n   ; align-items--normal\n:ai--s   ; align-items--start\n:ai--e   ; align-items--end\n:ai--b   ; align-items--baseline\n```\n\nNote that the enumerated value `none`, as well as global properties such as\n`inherit`, `initial`, `revert`, `unset`, etc. are intentially not supported with\nshorthand syntax:\n\n```Clojure\n;; This will NOT work\n:td--r\n\n;; This will work\n:td--revert ; =\u003e text-decoration: revert;\n```\n\u003c!-- TODO maybe just link to source? --\u003e\nSee the complete list of supported enum values\n[here](https://github.com/kushidesign/kushi/blob/main/doc/kushi-shorthand-reference.md).\n\n\u003cbr\u003e\n\n\u003c!-- ### Nested syntax\nYou can also you the 2-element vector form to \"nest\" styles, which is really\njust a way to dry up code and avoid repetition of the left half of the style:\n```Clojure\n(sx [\"has-ancestor(nav[data-foo-bar-sidenav][aria-expanded=\\\"true\\\"])\"\n     {:\u003e.sidenav-menu-icon:d  :none\n      :\u003e.sidenav-close-icon:d :inline-flex\n      :\u003eul:h                  \"calc((100vh - (var(--navbar-height) * 2)) * 1)\"\n      :h                      :fit-content\n      :o                      1}])\n```\nThe above would result in the following css:\n```css\n nav[data-foo-bar-sidenav][aria-expanded=\"true\"] ._1209178574\u003e.sidenav-menu-icon {\n  display: none;\n}\n\n nav[data-foo-bar-sidenav][aria-expanded=\"true\"] ._1209178574\u003e.sidenav-close-icon {\n  display: inline-flex;\n}\n\n nav[data-foo-bar-sidenav][aria-expanded=\"true\"] ._1209178574\u003eul {\n  height: calc((100vh - (var(--navbar-height) * 2)) * 1);\n}\n\n nav[data-foo-bar-sidenav][aria-expanded=\"true\"] ._1209178574 {\n  height: fit-content;\n  opacity: 1;\n}\n``` --\u003e\n\n\n\n\n## Working with media queries\n```Clojure\n;; Specify the font-size of an \u003ch1\u003e element across breakpoints\n[:h1\n (sx :fs--1.25rem\n     :md:fs--1.5rem\n     :lg:fs--1.75rem\n     :xl:fs--2rem)]\n```\nAs in the example above, you can use preceding modifiers to set different values\nfor a property at different breakpoints.\n\nKushi ships with the following, industry-standard, mobile-first breakpoint scale:\n```Clojure\n[:xsm {:min-width :480px}\n :sm {:min-width :640px}\n :md {:min-width :768px}\n :lg {:min-width :1024px}\n :xl {:min-width :1280px}\n :xxl {:min-width :1536px}]\n```\nBoth the names and values can be customized via supplying a kwargs vector (not a\nmap) as the `:media` entry in your `kushi.edn` config file. Because CSS Media\nQueries must be explicity ordered, this scale must be written as a vector of\nkwargs. See [Configuration Options](#configuration-options).\n\nBelow is an example of a scale that is desktop-first and uses different names.\u003cbr\u003e\nNote that in the case of desktop-first (`max-width`), the order is reversed\n(relative to mobile-first / `min-width`).\n```Clojure\n[:desktop {:max-width :1280px}\n :tablet {:max-width :1024px}\n :mobile {:max-width :768px}\n :small {:max-width :640px}]\n```\nAny media-query modifier that you use must correspond to a key in the breakpoint\nmap.\n\nWhen \"stacking\" other modifiers (such as psuedo-classes) in front of css props,\nthe media queries must always come first.\n\u003c!-- TODO: Provide example of such stacking --\u003e\n\n\u003cbr\u003e\n\n## Pseudos and Combo Selectors\nPseudo-classes, pseudo-elements, and combo selectors are available via modifiers:\n```Clojure\n[:div (sx :hover:c--blue\n          :\u003ea:hover:c--red\n          :\u0026_a:hover:c--gold ; The \"_\" gets converted to \" \"\n          :\u0026.bar:hover:c--pink\n          :before:fw--bold\n          :after:mie--5px\n          [\"~a:hover:c\" :blue] ; Vector is used as \"~\" is not valid in a keyword\n          [\"nth-child(2):c\" :red] ; Vector is used as \"(\" and \")\" are not valid in keywords\n          [:before:content \"\\\"⌫\\\"\"])\n [:a \"Erase\"]]\n```\nCSS resulting from the above example:\n```css\n.myns_core__L7C11 {\n  \u0026\u003ea {\n    \u0026:hover {\n      color: red;\n    }\n  }\n  \u0026 a {\n    \u0026:hover {\n      color: gold;\n    }\n  }\n  \u0026.bar {\n    \u0026:hover {\n      color: pink;\n    }\n  }\n  \u0026::after {\n    margin-inline-end: 5px;\n  }\n  \u0026~a {\n    \u0026:hover {\n      color: blue;\n    }\n  }\n  \u0026:nth-child(2) {\n    color: red;\n  }\n  \u0026::before {\n    font-weight: bold;\n    content: \"⌫\";\n  }\n  \u0026:hover {\n    color: blue;\n  }\n}\n```\n\n### Parents and ancestors\nKushi provides 2 fake css pseudo-classes in the form of `has-parent()` and\n`has-ancestor()`. With these, you to achieve further specificity with regards to parents and ancestors of the element that you are styling. This is useful when\nyou want to use styles that might change when a class is toggled or changed\nfurther up in the DOM.\n\n\n```Clojure\n(defn my-button [text]\n  [:button\n   (sx [\"has-ancestor(section.baz):color\" :blue]\n       [\"has-parent(section.dark):color\" :white]\n       {:on-click #(prn \"clicked!\")})\n     text])\n\n```\nThe above would result in the following css:\n```css\nsection.baz .myns_core__L7C11 {color: blue}\nsection.dark \u003e .myns_core__L7C11 {color: white}\n```\n\n### Targeting dark mode\nYou can use the `dark` modifier to define styles that are scoped to the dark\nthemes. This is sugar for `has-ancestor(.dark)`. It is assumed there will\npotentially be a class of `.dark` on an ancestor element in the DOM. This would\ntypically be the `\u003cbody\u003e` or the target element for the app.\n\n```Clojure\n(defn my-button [text]\n  [:button\n   (sx :dark:color--hotpink\n       :dark:b--2px:solid:hotpink\n       :dark:\u0026_.some-other-class:c--white\n       {:on-click #(prn \"clicked!\")})\n     text])\n\n```\nThe above would result in the following css:\n```css\n.dark .myns_core__L7C11 {color: hotpink; border: 2px solid hotpink}\n.dark .myns_core__L7C11 .some-other-class {color: white}\n```\n\nYou can use `kushi.ui.core/lightswitch!` to toggle a `.dark` class on the body,\nor a specific element of your choice.\n```Clojure\n(ns myns.core\n  (:require [kushi.ui.core :refer [lightswitch!]]))\n\n;; Toggle `.dark` class on body\n(lightswitch!)\n\n;; Toggle `.dark` class using querySelector\n(lightswitch! \"#my-id\")\n\n;; Any querySelector is valid and will work as long as it\n;; corresponds to an existing element in the DOM.\n(lightswitch! \"div.some-class\")\n\n```\n\u003cbr\u003e\n\n\u003c!-- ## Transparent Colors\nKushi offers a special syntax for adding transparency to colors. This will work\nwith any named css colors, hex colors, or any color that is part of Kushi's\nbuilt-in color scale (click on the \"Color\" section in sidemenu of the\n[interactive docs page](kushi.design) to view color scale).\n\n```Clojure\n;; With a css named color ...\n(sx :bgc--aliceblue/alpha-50)\n\n;; With a hex color ...\n(sx :bgc--00ff00/alpha-33)\n\n;; With a color from Kushi's design tokens color scale ...\n(sx :bgc--$purple-500/alpha-79)\n``` --\u003e\n\n\n\u003c!-- TODO - check and make sure this works as expected --\u003e\n## Selector Prefixing Options\nYou can narrow the specificity of you selectors by globally prepending a class\nor id (or any valid selector) of an ancestor element. Typically this would be\nsomething like the id of your \"app\" container.\n\n```Clojure\n;; In your kushi.edn map ...\n{:selector-prepend \"#my-app\"}\n\n;; In one of your component namespaces ...\n[:div\n (sx :c--red)]\n\n;; The above example would write the following rule to the css file:\n;; #my-app .myns_core__L7C11 {\n;;    color: red;\n;;}\n```\n\n\u003cbr\u003e\n\n## Defining CSS at-rules\n[at-rules](https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule) are CSS \nstatements that instruct CSS how to behave. You can define any at-rule with \n `kushi.core/defcss` by supplying a selector that starts with `@` character.\n\nNote - Although you can create global `@media` rules like this, `@media` rules\nare typically defined within `defcss`, `css`, and `sx`. Refer to the\n[Media queries](#media-queries) section for more details.\n\n\u003cbr\u003e\n\n### Defining @keyframes animations\n\nUse `kushi.core/defcss` with the keyword such as `\"@keyframes my-animation-name\"`\nto define CSS @keyframes animation. If you supply an `@keyframes` selector, the\nremainder of the arguments must be 2 element vectors consisting of a keyword or\nstring at index 0, followed by a stylemap at index 1. The keyword or string at\nindex 0 must be one of `#{:from \"from\" :to \"to\"}`, or a percentage from 0 - 100,\nexpressed as a keyword or string, e.g. `:50%` or `\"50%\"`.\n```Clojure\n;; This will twirl something on its y-axis\n(defcss \"@keyframes yspinner\"\n  [:0% {:transform \"rotateY(0deg)\"}]\n  [:100% {:transform \"rotateY(360deg)\"}])\n\n;; Somewhere in your component code...\n[:div\n (sx :animation--yspinner:12s:linear:infinite)\n \"Round \u0026 Round\"]\n\n;; ------------------------------------------------------------\n\n;; Another example, creating a class that will transitions color\n(defcss \"@keyframes blue-to-red\"\n  [:from {:color :blue}]\n  [:to {:color :red}])\n\n;; Somewhere in your shared styles code...\n(defcss blue-to-red :animation--blue-to-red:5s:linear)\n\n;; When you add the \"blue-to-red\" class on an element, it will\n;; transition the element's `color` property from blue to red.\n\n```\n\n\u003cbr\u003e\n\n\n### Adding Font Resources with @font-face\nYou can use `kushi.core/defcss` with a \"@font-face\" selector to load a local\nfont from a file. This will add an `@font-face` block to the css file generated\nby Kushi.\n\nThe `:src` entry must be a path (string), or vector of paths if you want to\nspecify multiple urls. The path(s) must be relative to the location of the\ngenerated css file. You could also use a remote url to load a hosted font file.\n\n```Clojure\n(defcss \"@font-face\"\n  {:font-family \"FiraCodeRegular\"\n   :font-weight \"400\"\n   :font-style \"normal\"\n   :src \"url(../fonts/FiraCode-Regular.woff)\"})\n```\n\n\u003cbr\u003e\n\n\u003c!--\n## Using Scales\nKushi ships with two different predefined scaling systems, which provide a scale\nof values for `width`, `font-size`, `padding`, `margin`, and `border-widths`.\n\nThese two systems shadow the scales provided by\n[Tachyons](http://tachyons.io/docs/typography/scale/) and\n [Tailwindcss](https://tailwindcss.com/docs/font-size).\n\nYou must explicitly opt-in to use one of the scales in your `kushi.edn` config\nfile:\n```Clojure\n{...\n :scaling-system :tachyons\n ...}\n\n; or the tailwind flavor\n\n{...\n :scaling-system :tailwind\n ...}\n```\n\nTo use values from these scales, supply a value affixed with an `*` to one of\nthe applicable css properties:\n```Clojure\n(sx :w--1*\n    :bw--2*\n    :fs--3*\n    :p--sm*\n    :m--md*)\n\n;; The above is equivalent to the following\n\n(sx :width--1rem\n    :border-width--.25rem\n    :font-size--1.5rem\n    :padding--.5rem\n    :margin--1rem)\n```\nView all the scale values\n[here](https://github.com/kushidesign/kushi/blob/main/src/kushi/scales.cljc).\n\u003cbr\u003e\n--\u003e\n\n## Injecting Stylesheets\n`kushi.inject/inject-stylesheet!` will inject a stylesheet, or a third-party\nstyle library into the head of your `index.html`. This is more of an edge case, as you would typically\njust do this with a `\u003clink\u003e` in your `index.html`. However, if your project uses a\nclj file to generate the contents of your `\u003chead\u003e` at build time, it may be\n handy to use this during development to inject new stylesheets without\n restarting your build.\n\n```Clojure\n(ns myapp.core\n  (:require\n   [kushi.inject :refer [inject-stylesheet!]]))\n\n(inject-stylesheet! {:rel \"stylesheet\"\n                     :href \"css/my-global-styles.css\"})\n```\n### Loading Google Fonts\nA more common use case for injecting a stylesheet would the loading of webfonts\nvia stylesheets, ala Google Fonts, or another similar webfonts service.\n\nYou can leverage `kushi.inject/add-google-fonts!` to simplify the process of\nadding Google fonts to your project.\n\nThe example below is a typical use case which loads a stylesheet from Google\nFonts.\n```Clojure\n(ns myapp.core\n  (:require\n   [kushi.inject :refer [add-google-fonts!]]))\n\n(add-google-fonts! {:family \"Playfair Display\"\n                    :styles {:normal [400 700]\n                             :italic [400 700]}})\n\n;; The above call is equivalent to the following:\n\n;; Note - the additional \"preconnect\" hints will improve Google Fonts performance.\n\n;; (inject-stylesheet {:rel \"preconnet\"\n;;                     :href \"https://fonts.gstatic.com\"\n;;                     :cross-origin \"anonymous\"})\n\n;; (inject-stylesheet {:rel \"preconnet\"\n;;                     :href \"https://fonts.googleapis.com\"})\n\n;; (inject-stylesheet {:rel \"stylesheet\"\n;;                     :href \"https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400;1,700\u0026display=swap\"})\n\n```\n\u003cbr\u003e\n\n`kushi.inject/add-google-fonts!` accepts any number of args, each one a single\nmap that represents a font-family and associated weights \u0026 styles. You can as\nmany different families as you want in a single go (although be mindful of\nperformance):\n\n\n```Clojure\n(ns myapp.core\n  (:require\n   [kushi.inject :refer [add-google-fonts!]]))\n\n(add-google-fonts! {:family \"Playfair Display\"\n                    :styles {:normal [400 700] :italic [400 700]}}\n                   {:family \"Lato\"\n                    :styles {:normal [100 400]}}\n                   {:family \"Pacifico\"\n                    :styles {:normal [400]}})\n```\n\u003cbr\u003e\n\n\n## Configuration Options\nVarious options are configurable via a required `kushi.edn` file.\n\nThis file must live in your project's root directory.\n\nThe only required entry in this map is `:css-dir`.\n\nFor a well commented starting point to build your own config,\n[the sample `kushi.edn` config from the Kushi Quickstart template](https://github.com/kushidesign/kushi-quickstart/blob/main/kushi.edn) (similar to below) is recommended.\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\n## Actionable Warnings\nIt is highly recommended to keep the terminal (that is running the `cljs-shadow`\nbuild process) visible so that you can catch warnings for malformed arguments to\nKushi functions.\n\nGiven the following:\n```Clojure\n(sx :.flex-col-c\n    :.absolute-fill\n    :h--100%\n    \"badstring\"\n    :m-10px\n    12\n    :ai--c\n    :bgc--black)\n```\n\nYou would receive warnings about invalid args in the terminal:\n\n\u003cdiv align=\"left\"\u003e\u003cimg src=\"docs/public/graphics/images/kushi-sx-bad-args-warnings.png\"\n width=\"550px\"/\u003e\u003c/div\u003e\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\u003c!-- ## Defining Components\nBelow is a contrived example of creating a reusable, stateless, and composable\ncomponent using `kushi.ui.core/defcom`.\n\n\n```Clojure\n(ns myapp.core\n  (:require\n   [kushi.core :refer [sx merge-attrs]]\n   [kushi.ui.core :refer [defcom]]))\n\n(defcom my-section\n  (let [{:keys [label label-attrs body-attrs]} \u0026opts]\n    [:section\n     (merge-attrs (sx :c--black)\n                  \u0026attrs\n     (when label [:div label-attrs label])\n     [:div body-attrs \u0026children]]))\n```\n\n`defcom` is a macro that returns a component rendering function which accepts an\noptional attributes map, plus any number of children. The signature at the call\nsite mirrros hiccup itself.\n\nUnder the hood, `defcom` pulls out any keys in attr map that start with `:-` and\nputs them in a separate `opts` map. This allows passing in various custom\noptions within the attributes map that will not clash with existing html\nattributes. This opts map can be referenced in the `defcom` body with the\n`\u0026opts` binding. `\u0026attrs` and `\u0026children` are also available. This\nampersand-leading naming convention takes its cue from the special `\u0026form` and\n`\u0026env` bindings used by Clojure's own `defmacro`.\n\nThe example above also uses `kushi.core/merge-attrs` to carefully merge\nattribute maps that are created with `kushi.core/sx`.\n\nAssuming your are using something like Reagent, you can use the resulting\n`my-section` component (from the above example) in your application code like so:\n\n```Clojure\n;; Basic, no label\n[my-section [:p \"Child one\"] [:p \"Child two\"]]\n\n;; With optional label\n[my-section (sx {:-label \"My Label\"}) [:p \"Child one\"] [:p \"Child two\"]]\n\n;; With all the options and additional styling\n[my-section\n (sx :.xsmall               ; Font-size utility class.\n     :p--1rem               ; Padding inside component.\n     :b--1px:solid-black    ; Border around component.\n     {:-label \"My Label\"\n      :-label-attrs (sx :.huge :c--red)\n      :-body-attrs (sx :bgc--#efefef)})\n [:p \"Child one\"]\n [:p \"Child two\"]]\n\n```\n\u003cbr\u003e --\u003e\n\n### Defining components\n\n\u003c!-- If, for some reason, you don't want use the `defcom` to define your complex\ncomponents, you can use the same underlying pattern that `defcom` abstracts. --\u003e\nKushi promotes a component definition pattern that mirrors hiccup itself by\nstandardizing the function signature as an (optional) single map of attributes\nfollowed by any number of children. This pattern relies on using the\n`kushi.ui.core/extract` helper function.\n\nUnder the hood, this helper function pulls out any keys in attributes map that\nstart with `:-` and puts them in a separate `opts` map. This allows passing in\nvarious custom options within the attributes map that will not clash with\nexisting html attributes. You can optionally make use of `kushi.core/merge-attrs`\nto enable decoration and composition of attribute maps.\n\n\n```Clojure\n;; This example assumes Reagent is being used as a rendering library\n(ns myapp.core\n  (:require\n   [kushi.core :refer [sx]]\n   [kushi.ui.core :refer [extract]]))\n\n(defn my-section\n  [\u0026 args]\n  (let [[opts attrs \u0026 children]  (extract args)\n        {:keys [label label-attrs body-attrs]} opts]\n    [:section\n     attrs\n     (when label [:div label-attrs label])\n     (into [:div body-attrs] children)]))\n```\n\nThe example above assumes the following:\n\n- The args list in the function definition is variadic\n- The optional attributes map may contain the custom attributes `:-label`,\n`:-label-attrs`, `:-body-attrs`.\n- The values of `:-label-attrs` and `:-body-attrs` are html attribute maps.\n\nThe helper function `kushi.ui.core/extract` will pull any keys prefixed\nwith `:-` out of the attributes map and into a user `opts` map. `extract`\nalways returns a map:\n```Clojure\n{:opts     {...}\n :attrs    {...}\n :children [...]}\n```\n\nOur `my-section` component function could be called like this:\n```Clojure\n(my-section \n (merge-attrs\n  (sx :border--1px:solid:black\n      :p--1rem)\n  {:id     \"my-id\"\n   :-label \"My custom label\"\n   :-label-attrs (sx :c--red)\n   :-body-attrs (sx :c--white :bgc--black)})\n  \"Child 1\"\n  \"Child 2\")\n```\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\u003c!-- ## Theming\nDetailed docs on theming coming soon...\n\u003cbr\u003e\n\n\u003cbr\u003e\n\n## Kushi Playground\nThe `kushi.playground` namespace exists to enable the generation of a clean,\ninteractive documentation site for all the UI in your project. You can customize\nthis with all your own custom components, branding, typography, colors and more.\nPlayground can be thought of as a lighter weight, ClojureScript-specific\nalternative to something like Storybook. Kushi's own UI documentation site at\n\u003ca href=\"https://kushi.design\"\u003ekushi.design\u003c/a\u003e is built using Playground.\n\nDetailed documentation for this feature is coming soon. In the meantime, you can\nperuse the `docs` dir in this repo which is the setup for the Kushi UI\ndocumentation site linked above.\n\n\u003cbr\u003e --\u003e\n\n## Usage with Build Tools\nAlthough Kushi is designed to be build-tool and framework agnostic, thus far it\nhas only been used in production with\n[Reagent](https://reagent-project.github.io/) +\n[Shadow-CLJS](https://github.com/thheller/shadow-cljs). Integrations and\nquickstart guides for using Kushi with other cljs rendering libraries is planned\nfor Q2 2025.\n\n### shadow-cljs\nSee the [kushi-quickstart](https://github.com/kushidesign/kushi-quickstart)\ntemplate for a detailed example of using Kushi in a shadow-cljs project.\n\n\u003cbr\u003e\n\n## Contributing\nFeel free to file issues or initiate discussion in\n\u003ca href=\"https://github.com/kushidesign/kushi/issues\" target=\"_blank\"\u003eIssues\u003c/a\u003e.\n\n\n\u003cbr\u003e\n\n\u003c!--\n## Roadmap\n...more info coming soon.\n\n\u003cbr\u003e\n\n## Development\n...more info coming soon.\n\n\u003cbr\u003e\n--\u003e\n## License\n\nCopyright © 2021-2025 Jeremiah Coyle\n\nDistributed under the EPL License. See LICENSE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkushidesign%2Fkushi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkushidesign%2Fkushi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkushidesign%2Fkushi/lists"}