{"id":16660177,"url":"https://github.com/brianchevalier/radiant","last_synced_at":"2025-04-09T18:42:54.343Z","repository":{"id":41559055,"uuid":"326189877","full_name":"BrianChevalier/radiant","owner":"BrianChevalier","description":"Write full featured CSS as Clojure data structures, inline","archived":false,"fork":false,"pushed_at":"2021-09-05T00:11:02.000Z","size":2666,"stargazers_count":19,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-23T20:43:44.269Z","etag":null,"topics":["clojure","clojurescript","css","css-compiler"],"latest_commit_sha":null,"homepage":"https://brianchevalier.github.io/radiant/","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BrianChevalier.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-02T13:34:48.000Z","updated_at":"2025-03-10T12:40:34.000Z","dependencies_parsed_at":"2022-09-21T12:50:18.838Z","dependency_job_id":null,"html_url":"https://github.com/BrianChevalier/radiant","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianChevalier%2Fradiant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianChevalier%2Fradiant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianChevalier%2Fradiant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrianChevalier%2Fradiant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BrianChevalier","download_url":"https://codeload.github.com/BrianChevalier/radiant/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248090358,"owners_count":21046070,"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","css-compiler"],"created_at":"2024-10-12T10:28:15.201Z","updated_at":"2025-04-09T18:42:54.321Z","avatar_url":"https://github.com/BrianChevalier.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Radiant\n\nWrite CSS styles with access to media queries and keyframe animations within [Hiccup](https://github.com/weavejester/hiccup) and all of CSS, inline. CSS styles are specified as pure Clojure data.\n\nHiccup already supports inline styling like the following, but is restricted to only styles that can be defined inline when writing HTML.\n\n``` clojure\n[:div {:style {:css-attribute \"value\"}}]\n```\n\nRadiant extends this idea of inline styling by using namespaced hiccup attributes to bring more of CSS's power inline. The following example shows using CSS media queries, pseudo-selectors and animation keyframes.\n\n```clojure\n[:div\n  {:style\n   {:color :black \n    :background :white}\n   :style/dark\n   {:background :black\n    :color :white}\n   :style/hover\n   {:color :red}\n   :style/keyframes\n   {:from {:background :blue}\n    :to {:background :green}}}\n  \"div content\"]\n```\n\nRadiant can be used to extract these inline styles and produce unique CSS classes to select the corresponding elements.\n\nSince these styles are written as data you only need to call functions from `radiant` when you're ready to generate CSS from data structures. Your styles can be defined independent of an API and can be serialized as EDN. They can also be handled as regular Clojure data. You can create functions that return styles, bindings for shared styles, or dynamically alter styles at runtime.\n\n## Translating Clojure data to CSS values\n\nAt it's core, Radiant provides a simple set of rules to translate Clojure data types into CSS (no more string bashing, or macros).\nBy default values are converted based on the type of the value according to the following:\n\n* string: passed through, without quotes\n  * `\"white\"` -\u003e `white`\n* keyword: passed through without preceding `:`\n  * `:color` -\u003e `color`\n* symbol: passed through as the name of the symbol (unqualified)\n  * `'symbol` -\u003e `symbol`\n* number: converted to string and px are added as the unit, unless the CSS key accepts dimensionless values\n  * `42` -\u003e `42px`\n* list: converted to CSS function call with comma separated arguments\n  * `'(linear-gradient :red :yellow :blue)` -\u003e `linear-gradient(red, yellow, blue)`\n* vector: space separated values (each value is processed by the same type dispatch)\n  * `[5 5]` -\u003e `5px 5px`\n  *  `'[(translateX 10) (translateY 20)]` -\u003e `translateX(10px) translateY(20px)`\n  * `'(circle [75 :at :center])` -\u003e `circle(75px at center)`\n\nThere are some cases where a CSS property may need to be handled differently. If your case is not covered in a particular release, you can extend the appropriate multimethod (see below).\n\nIf you need to perform a computation when writing a CSS function call, you can use Clojure's backtick to quote the list and tilde to execute code while in the list (example below). See [Syntax-quote](https://clojure.org/reference/reader#syntax-quote) for more details.\n\n``` clojure\n`(rgb ~(+ 10 x) 0 139)\n```\n\n## Example Usage\n\nOnce your Hiccup is written you can extract inline styles and return new hiccup in a wrapper div and `:style` with generated css.\n```clojure\n(radiant.hiccup/hoist-styles\n  [:div {:style {}} ...])\n=\u003e [:div\n     [:style \".class {...}\"]\n     [:div {:class \"class\"}]]\n```\n\nOr extract the Hiccup (with generated CSS classes) and a CSS string\n\n``` clojure\n(radiant.hiccup/extract-all-styles\n  [:div {:style {}}])\n=\u003e {:hiccup [:div {:class \"class\"}]\n    :css \".class {}...\"}\n```\n\nRadiant also provides some client-side reagent-compatible components (functions that just return hiccup). Styles are extracted, classes are generated, and inserted at the head of the DOM (styles are also cached).\n\n``` clojure\n[radiant.reagent/div\n  {:style/hover {:color :red}}\n  \"This is in a div\"]\n```\n\n\nYou can also write CSS with provided selectors as a Clojure Map and use similar syntax to inline styles.\n\n```clojure\n (radiant.core/style\n   {:h1\n    {:style {:color :red}}\n    #{:h2 :h3 :h4 :h5 :h6}\n    {:style {:color :black}\n     :style/hover {:color :red}\n     :style/dark {:color :blue}}\n    :div\n    {:style/dark\n     {:background-color :black\n     :color :white}}})\n```\n\n### Leverage Clojure\n\nProgramatically create and reuse styles similar to [tailwindcss](https://tailwindcss.com).\n\n``` clojure\n(def text-left {:text-align :left})\n(def text-right {:text-align :right})\n\n[:div {:style (merge {:color :red} text-left)\n       :style/small (merge {:color :red} text-right)}\n       \"Hello world!\"]\n```\n\nCompose styles as needed using all the power of Clojure\n\n``` clojure\n(def card {:style/small {:text-align :center}})\n(def focus {:style/hover {:color :red}})\n(def styles (partial merge-with merge))\n\n[:div (styles card focus)]\n```\n\n\n## CSS Selectors\n\nIf you need to write CSS not inline, you can match elements using selectors. Here's how to describe CSS selectors:\n\n* A single selector can be a string, keyword, or a symbol and will follow the same rules as above\n  * `:h1` -\u003e `h1 {...}`\n  * `'h1` -\u003e `h1 {...}`\n  * `\"h1\"` -\u003e `h1 {...}`\n* A set selectors becomes comma seperated selectors (i.e. the CSS body applied to each selector). Each selector in the set is translated based on the previous rule\n  * `#{:h1 :h2}` -\u003e `h1, h2 {...}`\n* A vector of selectors becomes a space seperated collection (i.e. descendant selectors)\n  * `[:div :h1]` -\u003e `div h1 {...}`\n* For child selectors do use a vector with `:\u003e` \n  * `[:div :\u003e :h1]` -\u003e `div \u003e h1`\n* For adjacent sibling selectors use a vector with `:+`\n  * `[:div :+ :p]` -\u003e `div + h1`\n\nIf your exact use case is not covered, you can always pass in a single string which will be pass through to the final CSS with no change.\n\n## Customization \u0026 Extension\n\nExtend `radiant.core/css` to define your own style handler. Use `radiant.core/selector` to generate a CSS selector. Note: these extension points are subject to change.\n\n``` clojure\n(defmethod css :style/custom\n  [sel k m]\n  ;; Your css generator here.\n  )\n```\n\n\nDefine how a CSS value is translated from Clojure data by extending the following multimethod.\n\n ``` clojure\n(defmethod normalize-css-value :css-property-name\n  [k v]\n  ;; return a string \n  ;; use `normalize-css-value` where necessary\n  )\n ```\n\n \n## FAQ\n\n* \"Aren't inline styles bad practice?\"\n  * Yes, typically inline styling is bad when working with regular HTML and CSS. It makes reusing styles impossible. With Clojure, you can easily factor out styles using all the tools you already know, without relying on CSS selectors.\n* \"What about semantic CSS?\"\n  * Using [semantic CSS](https://maintainablecss.com/chapters/semantics/) class names has been best practice for a long time, but a lot of developers are starting to [rethink this idea](https://adamwathan.me/css-utility-classes-and-separation-of-concerns/). Radiant doesn't preclude you from using semantic 'classes' (you can still use a single var to factor out a style per semantic component). Use whatever works best for you!\n* \"Why Radiant vs. x?\"\n  * Radiant is unique in that is does more to prescribe a Clojure *data structure* to describe CSS than to provide a particular set of functions. Radiant aims to be easily replaceable and not be coupled to your code. It also tries to stay as close to vanilla CSS as possible with as little Radiant-specific learning as possible.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianchevalier%2Fradiant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrianchevalier%2Fradiant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianchevalier%2Fradiant/lists"}