Skip to content

Commit

Permalink
Merge pull request #61 from Jarzka/feature/improved-at-rules
Browse files Browse the repository at this point in the history
Feature/improved-at-rules
  • Loading branch information
Jarzka authored Jun 13, 2021
2 parents 4c90820 + 1548987 commit 8e465fb
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 133 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 3.2.0

June 13, 2021

- `::stylefy/media` and `::stylefy/supports` can now be defined using vector syntax. This is useful if the order of the rules matter in CSS.
- All stylefy's special keywords now work inside `stylefy/supports` style map (manual mode and scoping were previously missing)

# 3.1.0

June 7, 2021
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ stylefy consists of modules that are optimised for specific UI libraries / frame
First, add stylefy core as a dependency:

```clj
[stylefy "3.1.0"]
[stylefy "3.2.0"]
```

Then, based on the UI library you are using, add a corresponding module. The main purpose of the module is to handle asynchronous DOM updates when components are rendered.
Expand Down Expand Up @@ -353,6 +353,13 @@ Define how your style looks on various screen sizes:
[:p "This is column 3"]]])
```

Alternative version using vector syntax. This is useful if the order of the rules matter in CSS.

```clojure
{::stylefy/media [[{:max-width phone-width} {:flex-direction :column}]
[{:max-width tablet-with} {:flex-direction :row}]]}
```

stylefy features supported in media queries: modes, scope and vendor prefixes.

For syntax help, see Garden's [documentation](https://github.com/noprompt/garden/wiki/Media-Queries).
Expand Down Expand Up @@ -380,6 +387,13 @@ Define how your style looks when certain CSS features are supported by the brows

```

Alternative version using vector syntax. This is useful if the order of the rules matter in CSS.

```clojure
{::stylefy/supports [["display: flex" {:color :green}]
["display: grid" {:color :purple}]]}
```

stylefy features supported in feature queries: modes, media queries and vendor prefixes.

## 3rd party classes
Expand Down
99 changes: 73 additions & 26 deletions examples/reagent/src/cljs/stylefy/examples/reagent/grid.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@
[stylefy.core :as stylefy :refer [use-style use-sub-style]]
[stylefy.examples.reagent.styles :as styles]))

(def grid-style {; Default style uses Flexbox as fallback
:display "flex"
:flex-direction "row"
:flex-wrap "wrap"
::stylefy/media {{:max-width styles/phone-width}
{:display "block"}}
; Use CSS Grid style if it is supported by the browser.
; If the browser does not support CSS Grid or feature queries at all, this
; block is simply ignored.
::stylefy/supports {"display: grid"
{:display "grid"
:grid-template-columns "1fr 1fr 1fr"
; Make CSS Grid responsive
::stylefy/media {{:max-width styles/phone-width}
{:grid-template-columns "1fr"}}}}})

(defn create-box-style [color]
; Map syntax

(def grid-style-map-syntax {; Default style uses Flexbox as fallback
:display "flex"
:flex-direction "row"
:flex-wrap "wrap"
:margin-bottom "1rem"
::stylefy/media {{:max-width styles/phone-width}
{:display "block"}}
; Use CSS Grid style if it is supported by the browser.
; If the browser does not support CSS Grid or feature queries at all, this
; block is simply ignored.
::stylefy/supports {"display: grid"
{:display "grid"
:grid-template-columns "1fr 1fr 1fr"
; Make CSS Grid responsive
::stylefy/media {{:max-width styles/phone-width}
{:grid-template-columns "1fr"}}}}})

(defn create-box-style-map-syntax [color]
{:background-color color
:width "33.333%"
:height "200px"
Expand All @@ -29,12 +32,56 @@
; Element width is always well managed by grid.
{:width "auto"}}})

(defn grid []
[:div (use-style grid-style)
[:div (use-style (create-box-style "#000000"))]
[:div (use-style (create-box-style "#AA0000"))]
[:div (use-style (create-box-style "#00AA00"))]
[:div (use-style (create-box-style "#0000AA"))]
[:div (use-style (create-box-style "#00AAAA"))]
[:div (use-style (create-box-style "#AAAA00"))]
[:div (use-style (create-box-style "#AA00AA"))]])
(defn grid-map-syntax []
[:<>
[:h3 "Map syntax"]
[:div (use-style grid-style-map-syntax)
[:div (use-style (create-box-style-map-syntax "#000000"))]
[:div (use-style (create-box-style-map-syntax "#AA0000"))]
[:div (use-style (create-box-style-map-syntax "#00AA00"))]
[:div (use-style (create-box-style-map-syntax "#0000AA"))]
[:div (use-style (create-box-style-map-syntax "#00AAAA"))]
[:div (use-style (create-box-style-map-syntax "#AAAA00"))]
[:div (use-style (create-box-style-map-syntax "#AA00AA"))]]])

; Vector syntax

(def grid-style-vector-syntax {; Default style uses Flexbox as fallback
:display "flex"
:flex-direction "row"
:flex-wrap "wrap"
:margin-bottom "1rem"
::stylefy/media {{:max-width styles/phone-width}
{:display "block"}}
; Use CSS Grid style if it is supported by the browser.
; If the browser does not support CSS Grid or feature queries at all, this
; block is simply ignored.
::stylefy/supports [["display: grid"
{:display "grid"
:grid-template-columns "1fr 1fr 1fr"
; Make CSS Grid responsive
::stylefy/media {{:max-width styles/phone-width}
{:grid-template-columns "1fr"}}}]]})

(defn create-box-style-vector-syntax [color]
{:background-color color
:width "33.333%"
:height "200px"
::stylefy/media {{:max-width styles/phone-width}
{:width "100%"}}
::stylefy/supports [["display: grid"
; Element width is always well managed by grid.
{:width "auto"}]]})

(defn grid-vector-syntax []
[:<>
[:h3 "Vector syntax"]
[:div (use-style grid-style-vector-syntax)

[:div (use-style (create-box-style-vector-syntax "#000000"))]
[:div (use-style (create-box-style-vector-syntax "#AA0000"))]
[:div (use-style (create-box-style-vector-syntax "#00AA00"))]
[:div (use-style (create-box-style-vector-syntax "#0000AA"))]
[:div (use-style (create-box-style-vector-syntax "#00AAAA"))]
[:div (use-style (create-box-style-vector-syntax "#AAAA00"))]
[:div (use-style (create-box-style-vector-syntax "#AA00AA"))]]])
65 changes: 45 additions & 20 deletions examples/reagent/src/cljs/stylefy/examples/reagent/main.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -147,24 +147,47 @@
[bs-navbar-item 0 active-index "Hello"]
[bs-navbar-item 1 active-index "World!"]])))

(defn- responsive-layout []
[:div (use-style styles/responsive-layout)
; The easiest way to use a sub-style is to call use-sub-style function:
[:div (use-sub-style styles/responsive-layout :column1)
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]]
[:div (use-sub-style styles/responsive-layout :column2)
[:p "This is column 2"]
[:p "This is column 2"]
[:p "This is column 2"]]
[:div (use-sub-style styles/responsive-layout :column3)
; If there are more than one level of sub-style nesting, call sub-style to get
; the desired sub-style and use it.
[:p (use-style (sub-style styles/responsive-layout :column3 :text)) "This is column 3"]
[:p (use-style (sub-style styles/responsive-layout :column3 :text)) "This is column 3"]]])
(defn- responsive-layout-map-syntax []
[:<>
[:h3 "Map syntax"]
[:div (use-style styles/responsive-layout-map-syntax)
; The easiest way to use a sub-style is to call use-sub-style function:
[:div (use-sub-style styles/responsive-layout-map-syntax :column1)
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]]
[:div (use-sub-style styles/responsive-layout-map-syntax :column2)
[:p "This is column 2"]
[:p "This is column 2"]
[:p "This is column 2"]]
[:div (use-sub-style styles/responsive-layout-map-syntax :column3)
; If there are more than one level of sub-style nesting, call sub-style to get
; the desired sub-style and use it.
[:p (use-style (sub-style styles/responsive-layout-map-syntax :column3 :text)) "This is column 3"]
[:p (use-style (sub-style styles/responsive-layout-map-syntax :column3 :text)) "This is column 3"]]]])

(defn- responsive-layout-vector-syntax []
[:<>
[:h3 "Vector syntax"]
[:div (use-style styles/responsive-layout-vector-syntax)
; The easiest way to use a sub-style is to call use-sub-style function:
[:div (use-sub-style styles/responsive-layout-vector-syntax :column1)
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]
[:p "This is column 1"]]
[:div (use-sub-style styles/responsive-layout-vector-syntax :column2)
[:p "This is column 2"]
[:p "This is column 2"]
[:p "This is column 2"]]
[:div (use-sub-style styles/responsive-layout-vector-syntax :column3)
; If there are more than one level of sub-style nesting, call sub-style to get
; the desired sub-style and use it.
[:p (use-style (sub-style styles/responsive-layout-vector-syntax :column3 :text)) "This is column 3"]
[:p (use-style (sub-style styles/responsive-layout-vector-syntax :column3 :text)) "This is column 3"]]]])

(defn animation []
[:div (use-style styles/animated-box)])
Expand Down Expand Up @@ -327,7 +350,8 @@

[:h2 "Simple responsive layout"]
[:p "stylefy supports media queries out of the box"]
[responsive-layout]
[responsive-layout-map-syntax]
[responsive-layout-vector-syntax]

[:h2 "Animations"]
[:p "stylefy also supports keyframes"]
Expand All @@ -343,7 +367,8 @@

[:h2 "Feature queries"]
[:p "The following example is rendered using CSS Grid if supported by the browser. If not, it uses Flexbox fallback as the default style. stylefy also supports media queries inside feature queries!"]
[grid/grid]
[grid/grid-map-syntax]
[grid/grid-vector-syntax]

[garden-units]

Expand Down
20 changes: 19 additions & 1 deletion examples/reagent/src/cljs/stylefy/examples/reagent/styles.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
(def column {:padding "5px"
:color "white"})

(def responsive-layout
(def responsive-layout-map-syntax
{:display :flex
:flex-direction :row
::stylefy/sub-styles
Expand All @@ -100,6 +100,24 @@
::stylefy/media {{:max-width phone-width} {:background-color "#000000"}}})}
::stylefy/media {{:max-width phone-width} {:display :block}}})

(def responsive-layout-vector-syntax
{:display :flex
:flex-direction :row
::stylefy/sub-styles
{:column1 (merge column
{:background-color "#AA0000"
:flex 1})
:column2 (merge column
{:background-color "#00AA00"
:flex 2})
:column3 (merge column
{:background-color "#0000AA"
:flex 1
; sub-styles can also contain sub-styles
::stylefy/sub-styles {:text {:color "red"}}
::stylefy/media [[{:max-width phone-width} {:background-color "#000000"}]]})}
::stylefy/media {{:max-width phone-width} {:display :block}}})

(def animated-box (merge simple-box
{:animation-name "simple-animation"
:animation-duration "3s"
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject stylefy "3.1.0"
(defproject stylefy "3.2.0"
:description "Library for styling UI components"
:url "https://github.com/Jarzka/stylefy"
:dependencies [[org.clojure/clojure "1.9.0"]
Expand Down
82 changes: 34 additions & 48 deletions src/cljc/stylefy/impl/conversion.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
[clojure.string :as str]
[garden.compiler :as compiler]))

(declare style->css)

(defn garden-units->css
"Checks all values in the map and converts all Garden units to CSS."
[props]
Expand Down Expand Up @@ -107,62 +109,46 @@
stylefy features supported in media query style map:
- modes
- scope
- vendor prefixes
stylefy/manual is not supported here since one can use it to create
::stylefy/manual is not supported here since one can use it to create
media queries."
; TODO Media queries could also be defined in a vector (just like modes can be defined as a map or vector)
[{:keys [props hash custom-selector] :as _style} options]
(when-let [stylefy-media-queries (:stylefy.core/media props)]
(when-let [media-queries (:stylefy.core/media props)]
(let [css-selector (or custom-selector (class-selector hash))
css-media-queries (map
(fn [media-query]
(let [media-query-props (get stylefy-media-queries media-query)
media-query-css-props (utils/remove-special-keywords media-query-props)
scoped-styles-garden (map
(fn [scoping-rule]
(find-and-handle-scoped-style-map scoping-rule css-selector))
(:stylefy.core/scope media-query-props))
garden-class-definition [css-selector media-query-css-props]
garden-pseudo-classes (convert-stylefy-modes-to-garden media-query-props)
garden-vendors (convert-stylefy-vendors-to-garden media-query-props)
garden-options (or (merge options garden-vendors) {})]
(css garden-options [(at-media media-query (into garden-class-definition
garden-pseudo-classes))
(at-media media-query scoped-styles-garden)])))
(keys stylefy-media-queries))]
handle-media-query (fn [media-query media-query-props]
(let [media-query-css-props (utils/remove-special-keywords media-query-props)
scoped-styles-garden (map
(fn [scoping-rule]
(find-and-handle-scoped-style-map scoping-rule css-selector))
(:stylefy.core/scope media-query-props))
garden-class-definition [css-selector media-query-css-props]
garden-pseudo-classes (convert-stylefy-modes-to-garden media-query-props)
garden-vendors (convert-stylefy-vendors-to-garden media-query-props)
garden-options (or (merge options garden-vendors) {})]
(css garden-options [(at-media media-query (into garden-class-definition
garden-pseudo-classes))
(at-media media-query scoped-styles-garden)])))
css-media-queries (cond
(map? media-queries) (mapv #(handle-media-query % (get media-queries %)) (keys media-queries))
(vector? media-queries) (mapv #(handle-media-query (first %) (second %)) media-queries))]
(apply str css-media-queries))))

(defn- convert-feature-queries
"Converts stylefy/supports definition into CSS feature query.
stylefy features supported in feature query style map:
- modes
- media queries
- vendor prefixes"
; TODO Manual mode and scoping should also be supported here
[{:keys [props hash custom-selector] :as _style} options]
(when-let [stylefy-supports (:stylefy.core/supports props)]
(let [css-selector (or custom-selector (class-selector hash))
css-supports (map
(fn [supports-selector]
(let [supports-props (get stylefy-supports supports-selector)
supports-css-props (utils/remove-special-keywords supports-props)
garden-class-definition [css-selector supports-css-props]
garden-pseudo-classes (convert-stylefy-modes-to-garden supports-props)
garden-vendors (convert-stylefy-vendors-to-garden supports-props)
garden-options (or (merge options garden-vendors) {})
css-media-queries-inside-supports (convert-media-queries
{:props supports-props
:hash hash
:custom-selector custom-selector}
options)]
(str "@supports (" supports-selector ") {"
(css garden-options (into garden-class-definition
garden-pseudo-classes))
css-media-queries-inside-supports
"}")))
(keys stylefy-supports))]
"Converts stylefy/supports definition into CSS feature query."
[{:keys [hash props custom-selector] :as _style} options]
(when-let [supports-rules (:stylefy.core/supports props)]
(let [handle-supports-rule (fn [supports-query supports-props]
(str "@supports (" supports-query ") {"
(style->css {:props supports-props
:hash hash ; Hash of the whole map
:custom-selector custom-selector}
options)
"}"))
css-supports (cond
(map? supports-rules) (mapv #(handle-supports-rule % (get supports-rules %)) (keys supports-rules))
(vector? supports-rules) (mapv #(handle-supports-rule (first %) (second %)) supports-rules))]
(apply str css-supports))))

(defn- convert-manual-styles
Expand Down
Loading

0 comments on commit 8e465fb

Please sign in to comment.