Code Monkey home page Code Monkey logo

girouette's Introduction

Girouette CircleCI

Clojars Project Cljdoc badge Project chat Clojars download_badge

Une girouette

Dès que le vent soufflera, je repartira.
Dès que les vents tourneront, nous nous en allerons.

Girouette is a grammar-based, generative approach to CSS. It translates a classname into a Garden data structure representing CSS.

(class-name->garden "w-10")
; => [".w-10" {:width "2.5rem"}]

Girouette also makes it easy to use your own grammar rules to generate anything you may dream of.

Introduction

CSS libraries like Tailwind and Tachyons provide quantities of predefined class names, hoping to cover most of the needs of their users. But because of combinatory explosion, they cannot provide all the class names users will ever need, in which case the users will have to hack their way with config files and/or handwritten CSS. When releasing for production, the unused CSS classes are removed from the CSS files using tools like PurgeCSS.

Girouette is taking the opposite approach where we first look at which class names are used, and from their names and a grammar associated with some generation rules, the corresponding CSS content is created.

This "on demand" generative approach allows to have any combination of parameters in a CSS class name, while opening the door to the most creative grammars which a human would want to use to communicate its intent.

(UPDATE: a couple of months after Girouette was released, the author of Tailwind implemented in its version 2 and 3 an "on demand" feature very similar to Girouette)

Documentation & Resources

Girouette currently has components which makes it compatible (with a few caveats) with:

Presentation @ the Bay Area Clojure Meetup:

The project has example projects in example/:

How it works

Girouette is using the awesome Instaparse library for parsing the class names, and is converting them into the Garden format.

Its API mainly consists in the function class-name->garden which is pretty explicit.

(class-name->garden "w-42%")
;=> [".w-42\\%" {:width "42%"}]

You can use Girouette processor tool to extract the CSS class names from your source code and generate the CSS in real time as you develop.

See the demo project for more information.

Advantages of this approach

With the right Girouette components in place, any parameters can be combined in class names without leaving your usual REPL workflow.

Large range on numbers

No need to stop what you are doing and to modify some config files just because mx-13 is not supported by default while mx-12 is.

Any color can be represented directly in class names, like rgba-f59d or rgba-ff5599dd.

Limitless class name descriptiveness

It is possible to create grammars which support very long class names.

;; Example of class name:
"bg-gradient-to-right-red-orange-yellow-green-blue-purple"

;; Instaparse rule:
"bg-gradient = <'bg-gradient-to-'> gradient-direction (<'-'> color)+
 gradient-direction = 'top' | 'right' | 'bottom' | 'left' | angle
 color = ...
 "

Link to other CSS projects

In Clojure

A few other alternatives are available.

In JS

Atomizer

Atomizer is an older project which is also interpreting CSS class names.

WindiCSS

Independently and in parallel of Girouette's development, WindiCSS was developed with similar ideas. Please check it out, specially if you are developing directly in the JS environment.

Who is using Girouette

(To add your project to this list, just edit this file and open a pull request)

Contribute

Contributions are very welcome, just make sure that the contributions are your own, and add proper credits in the commit messages if it is not the case.

License terms

This project is distributed under the MIT License, which is available at https://opensource.org/licenses/MIT

Copyright (c) Vincent Cantin and contributors.

girouette's People

Contributors

andrei-zhidkov avatar dpassen avatar flyingmachine avatar green-coder avatar hysenbug avatar jacobobryant avatar jamesnvc avatar jerems avatar paintparty avatar philippamarkovics avatar prestancedesign avatar raphaelsaunier avatar vynlar avatar wildwestrom avatar zolazhou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

girouette's Issues

Dark mode variant does not work for text

I added a test case (see philippamarkovics@41acf0e) that shows dark mode being correctly applied for backgrounds but not for text. Looking at the generated stylesheet, it seems selectors are sorted alphabetically with the dark-mode media queries coming before the .text- classes. I would assume outputting the media queries last should fix this.

Upgrade to latest Tailwind v3

The README mentions tailwind 2.0.2, the latest release is 3.0.17.

What would it take to upgrade girouette? Would financial support help?

[Processor tool] Add the "comprehensive" CSS class name retrieval mode

This mode collects anything that look like it could be a CSS class name, including:

  • All the strings
  • All the keywords whose name matches the structure of a hiccup tag, regardless of where they are in the source code.

This way to collect the class names will create a lot of false positives, but it will be mitigated by the fact that:

  • only the names which are matching the grammar will be kept,
  • generating CSS for a few unused class names won't be a big issue in most cases.

This mode is suitable for users working on prototypes and users not having a serious need for having the smallest possible CSS file.

Add `:version` field to components

The goal is to be able to select what components to include in the grammar based on the version's value.

Example of version value: [:tw 3]

Add support for css variables

We may add a grammar for setting css variables, for example:

(* Matches "set-var-foobar-#fff" *)
set-css-variable = <'set-var-'> css-variable-name <'-'> encoded-css-value

or to read a css variable:

read-css-variable = <'var-'> css-variable-name

We may also add the read-css-variable in places where the user may want to use them. Not sure if we should add support for it everywhere or just start with places which require a color and experiment on that.

Should it be girouette.tw to tw.girouette?

While I completely agree that this great ns, tw, should stay independent from larger ns structures, wouldn't it make sense for namespaces of this library to follow a two-level, reverse-domain naming strategy?

From memory, following the now standard strategy of Clojars would name the library coordinate com.github.green-coder/girouette whilst the user could require namespaces like tw.girouette.* (the two are independent).

I won't troll on renaming tw into cn-tpe.

Display rule includes variants in value

For example: .sm:flex => {:display "sm:flex"}

I'm using this quick fix to get around it; not sure if there's a better way:

    :garden (fn [{:keys [class-name]}]
              (let [class-name (str/replace class-name #".*:" "")] ; added this line
                {:display (if (= "hidden" class-name)
                            "none"
                            class-name)}))

Invalid CSS is generated for `{screen}:display` rules

First of all, thanks a lot for making this library! It's very convenient and fun to use

I noticed an issue when using classes for responsive display properties, such as sm:block, md:table-cell, etc.

For example, sm:flex generates the following CSS:

@media (min-width: 640px) {

  .sm\:flex {
    display: sm:flex;
  }

}

Which should be:

@media (min-width: 640px) {

  .sm\:flex {
    display: flex;
  }

}

Road map to v0.0.1

Tasks not required for the v0.0.1 release (they can be done later):

  • Achieve 100%-ish compatibility with Tailwind v2.0.2 (issue #21)
  • Improve utility functions to make it easy to create own grammar rules (issue #34)
  • Improve class names gathering from source code (issue #28) will be done in a separate library
  • Pre-release clean up, source code design review, documentation
  • Release and deploy on Clojars

Tasks not required for the v0.0.1 release (they can be done later):

  • Demo: run Girouette in frontend land, regenerate and set CSS between reloads (issue #26)
  • Implement the @apply equivalent, to combine class names together (issue #35)
  • Leverage Girouette's approach to incorporate grammar for animation (issue #31)
  • Make Girouette production ready, consider CSS minifying scenarios

Improve the gathering of the CSS classes in the source code

This is what I have in mind:

(with-class-names-meta
  (defn foobar ...))

I think that this pattern would compose better with any existing defn-ish way people use to define their Clojure root variables, and it has the merit do be explicit.

Any way to output colors without css vars? (or maybe as hex or rgb)

I am trying to use Girouette + Ornament for email html generation, and the email clients are retrograde and have spotty support for newer CSS features like RGBA or CSS vars.

This is in the context of an ornament build, but it is only happening on Girouette tokens...

(defstyled :div :text-black
leads to these color rules in style tag
--gi-text-opacity:1;color:rgba(255,255,255,var(--gi-text-opacity))

Option to render to 6digit hex is the most bulletproof for email, but obviously that doesn't account for transparency in abstraction...

I noticed your new Tailwind 3 color spec has hex values. But the current clojars releas does not include this. I tried using a deps.edn git dependency, but the project does not seem to be structured to support that.

Make a demo with CSS generation in frontend land

Girouette, Instaparse and Garden are all written in CLJC, which means that it should be possible to generate the CSS content while running in the browser.

That could be a good development setup, as it would be independent from the various build tools which the Clojure community enjoys.

divide-* bug

I'm running into an issue when using girouette via https://github.com/lambdaisland/ornament. Apologies if I should be reporting this in that project instead of this one

Given the following:

(ns donut.todo-example.frontend.components.todo-list.list)

(o/defstyled ul :div
  :max-w-7xl :mx-auto :sm:px-6 :lg:-px-8
  [:>.list-container :bg-white :shadow :overflow-hidden :sm:rounded-md]
  [:>.list-container>ul :divide-y :divide-gray-200]
  [:>.list-container>ul>li :p-3]
  ([& list-items]
   [:<>
    (into
     [:div.list-container
      (into
       [:ul {:role "list"}]
       list-items)])]))

(defn component
  []
  [:div "my list"
   [ui/ul
    [:li "list item 1"]
    [:li "list item 2"]
    [:li "list item 3"]
    [:li "list item 4"]
    [:li "list item 5"]]])

This css gets produced:

.donut_todo-example_frontend_components_todo-list_list__ul>.list-container>ul>*+* {
    border-top-width: calc(1px * var(--gi-divide-y-reverse));
    border-bottom-width: calc(1px * calc(1 - var(--gi-divide-y-reverse)));
}

This rule references the var --gi-divide-y-reverse, but that var isn't generated.

Additionally, these rules look different from those generated by tailwind:

.divide-y > :not([hidden]) ~ :not([hidden]) {
    --tw-divide-y-reverse: 0;
    border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
    border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
}

I guess they're functionally the same, but thought I'd share anyway :)

Thanks for this cool library!

Generate class lists

Hi!
I deal with documents that consist of hiccup that might contain arbitrary tailwind markup. I therefore would like to use girouette to generate one or more css files.
I was able to generate css for individual styles, and this works really wonderful once one knows the list of tags.

What I would like to have is a way to create a css file based on possible combinations.

This idea I think is also a good way to enforce proper style template guides, or in other words make sure that multiple documents use the same styles / have a common design philosophy. If everything is possible then at some point there is chaos and nothing works.

Thanks

Add Tailwind's v3 colors

We need a new list of colors beside girouette.tw.color/default-color-map representing the default colors of Tailwind v3.

README a bit outdated

CSS libraries like Tailwind and Tachyons provide quantities of predefined class names, hoping to cover most of the needs of their users. But because of combinatory explosion, they cannot provide all the class names users will ever need, in which case the users will have to hack their way with config files and/or handwritten CSS. When releasing for production, the unused CSS classes are removed from the CSS files using tools like PurgeCSS.

Just to be factually correct, Tailwind supports “on-demand” class generation (which it calls JIT) since v2 , and since v3 it’s the default method.

Reload garden-fn on file change

I've made the following quick hack to girouette.processor so that I don't have to restart the process when I change the definition of class-name->garden (which will probably be pretty often now given #35 (comment)). Would you be interested in a PR for this or something similar?

...

(defn- garden-fn-file? [^File file garden-fn-sym]
  (str/ends-with?
    (str/replace (.getPath file) #"\.clj(c|s)?" "")
    (str/replace (namespace garden-fn-sym) "." "/")))

...

  (let [garden-fn-sym garden-fn
        garden-fn (cond-> garden-fn

...

      (hawk/watch! [{:paths source-paths
                     :handler (fn [ctx {:keys [^File file kind]}]
                                (when (garden-fn-file? file garden-fn-sym)
                                  (require (symbol (namespace garden-fn-sym)) :reload)
                                  (swap! config assoc :garden-fn (requiring-resolve garden-fn-sym)))

...

I tried :reload-all instead of :reload, but it was giving me a weird error (java.lang.UnsupportedOperationException: Can't create empty: garden.selectors.CSSSelector), perhaps something to do with reloading girouette namespaces? :reload is fine for my purposes, but it could be a gotcha since it wouldn't load changes from any required namespaces; just garden-fn's namespace. tools.namespace's refresh might work.

Processor tool improvements

  • Add an option to prepend Preflight when the output is garden or css.
  • In verbose mode, when a CSS class name does not match the grammar, it should display in blue inside [-? ...] and [+? ...] blocks.
  • It should treat each strings as a list of css class names separated by spaces and discard empty strings from its class name collection.
  • It should not display compilation warnings or errors.
  • Add emojis

Add option for dry-run on the preprocessor

The option should return right after displaying the options used by the preprocessor.

This can be convenient to quickly see what are the default values for the options without really running the tool.

Demo project throws parse error

I'm getting this from the demo project:

$ clojure -X:girouette
Execution error at instaparse.util/throw-runtime-exception (util.clj:7).
Error parsing grammar specification:
Parse error at line 320, column 1:

^
Expected one of:
!
&
ε
eps
EPSILON
epsilon
Epsilon
<
(
{
[
#"#\"[^\"\\]*(?:\\.[^\"\\]*)*\"(?x) #Double-quoted regexp"
#"#'[^'\\]*(?:\\.[^'\\]*)*'(?x) #Single-quoted regexp"
#"\"[^\"\\]*(?:\\.[^\"\\]*)*\"(?x) #Double-quoted string"
#"'[^'\\]*(?:\\.[^'\\]*)*'(?x) #Single-quoted string"
(*
#"[^, \r\t\n<>(){}\[\]+*?:=|'"#&!;./]+(?x) #Non-terminal"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.