encre_css/
lib.rs

1//! A TailwindCSS-compatible utility-first CSS generation library written in Rust.
2//!
3//! ## A brief introduction to utility-first CSS frameworks
4//!
5//! Traditionally, whenever you need to style something on the web, you write CSS in a dedicated
6//! file and apply the rules using classes in your HTML, like that:
7//!
8//! <div style="margin-top: 1rem; margin-bottom: 1rem;"></div>
9//!
10//! <div class="notification">
11//!   <div class="notification-header">
12//!     <div class="app-icon"></div>
13//!     A new Javascript library has been released!
14//!   </div>
15//!   <div class="notification-body">
16//!     The library <code>react</code> has just been released, did you know it?
17//!     It is <i>a JavaScript library for creating user interfaces</i>.
18//!   </div>
19//!   <div class="notification-footer">
20//!     <a href="#" class="dismiss-button">Dismiss</a>
21//!     <a href="#" class="try-button">Try it here!</a>
22//!   </div>
23//! </div>
24//!
25//! <div style="margin-top: 1rem; margin-bottom: 1rem;"></div>
26//!
27//! <style>
28//! .notification {
29//!   width: 30rem;
30//!   box-shadow: 1px 1px 5px 2px #e5e7eb;
31//!   border-radius: 0.8rem;
32//!   font-family: sans-serif;
33//! }
34//!
35//! .notification-header {
36//!   padding: 0.5rem 1rem;
37//!   display: flex;
38//!   align-items: center;
39//! }
40//!
41//! .app-icon {
42//!   background-color: rgb(37 99 235);
43//!   border-radius: 50%;
44//!   width: 1.2rem;
45//!   height: 1.2rem;
46//!   margin-right: 1rem;
47//! }
48//!
49//! .notification-body {
50//!   padding: 1rem 1.5rem 1.5rem 1.5rem;
51//! }
52//!
53//! .notification-footer {
54//!   display: flex;
55//!   justify-content: space-between;
56//! }
57//!
58//! .dismiss-button {
59//!   color: rgb(225 29 72);
60//!   padding: 0.5rem 1rem;
61//! }
62//!
63//! .try-button {
64//!   background-color: rgb(37 99 235);
65//!   color: white;
66//!   border-bottom-right-radius: 0.8rem;
67//!   border-top-left-radius: 0.8rem;
68//!   padding: 0.5rem 1rem;
69//!   box-shadow: 1px 1px 5px 1px rgb(37 99 235);
70//! }
71//! </style>
72//
73//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">&lt;div</span> class=<span class="string">"notification"</span><span class="kw">&gt;
74//!   &lt;div</span> class=<span class="string">"notification-header"</span><span class="kw">&gt;</span>
75//!     <span class="kw">&lt;div</span> class=<span class="string">"app-icon"</span><span class="kw">&gt;&lt;/div&gt;</span>
76//!     A new Javascript library has been released!
77//!   <span class="kw">&lt;/div&gt;</span>
78//!   <span class="kw">&lt;div</span> class=<span class="string">"notification-body"</span><span class="kw">&gt;</span>
79//!     The library <span class="kw">&lt;code&gt;</span>react<span class="kw">&lt;/code&gt;</span> has just been released, did you know it?
80//!     It is <span class="kw">&lt;i&gt;</span>a JavaScript library for creating user interfaces<span class="kw">&lt;/i&gt;</span>.
81//!   <span class="kw">&lt;/div&gt;</span>
82//!   <span class="kw">&lt;div</span> class=<span class="string">"notification-footer"</span><span class="kw">&gt;</span>
83//!     <span class="kw">&lt;a</span> href=<span class="string">"#"</span> class=<span class="string">"dismiss-button"</span><span class="kw">&gt;</span>Dismiss<span class="kw">&lt;/a&gt;</span>
84//!     <span class="kw">&lt;a</span> href=<span class="string">"#"</span> class=<span class="string">"try-button"</span>&gt;Try it here!<span class="kw">&lt;/a&gt;</span>
85//!   <span class="kw">&lt;/div&gt;</span>
86//! <span class="kw">&lt;/div&gt;</span>
87//! </code></pre></div>
88//!
89//! However styling this way is pretty boring because you need to think of good class names and
90//! you have to repeatedly switch between several files, it could be better. Utility-first CSS
91//! frameworks takes a new approach by using minimal and pre-defined class names directly linked to
92//! its CSS rule content. The CSS file will then be generated
93//! [on-demand](https://antfu.me/posts/reimagine-atomic-css#on-demand-way) allowing the classes
94//! to be very flexible and customizable. This approach lets you quickly prototype visual HTML
95//! elements and encourages you to turn them later into components using your favorite web framework.
96//! It also makes building a responsive website easier and forces it to be closer to your design
97//! system:
98//
99//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">&lt;div</span> class=<span class="string">"w-128 text-md shadow-[1px_1px_10px_2px_#e5e7eb] rounded-xl"</span><span class="kw">&gt;
100//!   &lt;div</span> class=<span class="string">"p-3 flex items-center"</span><span class="kw">&gt;</span>
101//!     <span class="kw">&lt;div</span> class=<span class="string">"bg-blue-500 rounded-full w-5 h-5 mr-3"</span><span class="kw">&gt;&lt;/div&gt;</span>
102//!     A new Javascript library has been released!
103//!   <span class="kw">&lt;/div&gt;</span>
104//!   <span class="kw">&lt;div</span> class=<span class="string">"p-6 pt-4"</span><span class="kw">&gt;</span>
105//!     The library <span class="kw">&lt;code&gt;</span>react<span class="kw">&lt;/code&gt;</span> has just been released, did you know it?
106//!     It is <span class="kw">&lt;i&gt;</span>a JavaScript library for creating user interfaces<span class="kw">&lt;/i&gt;</span>.
107//!   <span class="kw">&lt;/div&gt;</span>
108//!   <span class="kw">&lt;div</span> class=<span class="string">"flex justify-between"</span><span class="kw">&gt;</span>
109//!     <span class="kw">&lt;a</span> href=<span class="string">"#"</span> class=<span class="string">"p-3 text-rose-600"</span><span class="kw">&gt;</span>Dismiss<span class="kw">&lt;/a&gt;</span>
110//!     <span class="kw">&lt;a</span> href=<span class="string">"#"</span> class=<span class="string">"p-3 bg-blue-600 text-white rounded-br-xl rounded-tl-xl shadow shadow-blue-600"</span>&gt;Try it here!<span class="kw">&lt;/a&gt;</span>
111//!   <span class="kw">&lt;/div&gt;</span>
112//! <span class="kw">&lt;/div&gt;</span>
113//! </code></pre></div>
114//!
115//! There is already a lot of utility-first frameworks like [Tailwind
116//! CSS](https://tailwindcss.com), [Windi CSS](https://windicss.org), [Twind](https://twind.dev)
117//! and [Uno CSS](https://uno.antfu.me), but `encre-css` is unique because it is written in Rust and
118//! uses a new architecture, making it **the fastest utility-first framework** (according to the
119//! benchmark [here](https://gitlab.com/encre-org/encre-css-bench) based on
120//! [Uno CSS' benchmark](https://github.com/unocss/unocss/tree/main/bench)). It is also very
121//! [customizable](crate::plugins).
122//!
123//! ## Getting started
124//!
125//! Add `encre-css` to your `Cargo.toml`:
126//!
127//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[dependencies]</span>
128//! encre-css = <span class="string">"0.19.1"</span></code></pre></div>
129//!
130//! Generating styles takes two steps:
131//! - You need to _configure_ the CSS generation by making a [`Config`] structure.
132//!   It can be created by reading a [TOML](https://toml.io) file using
133//!   [`Config::from_file`] or by using the default values with [`Config::default`];
134//! - Then, you need to _generate the styles_ based on some sources using the [`generate`]
135//!   function. This function will scan the content of the sources, extract atomic classes and
136//!   generate the style needed for each class.
137//!
138//! ### Example
139//!
140//! ```
141//! use encre_css::Config;
142//!
143//! let config = Config::default();
144//! let generated = encre_css::generate(
145//!     [r#"<p class="w-auto bg-red-200 rounded-md">Hello world!</p>"#],
146//!     &config,
147//! );
148//!
149//! assert!(generated.ends_with(".w-auto {
150//!   width: auto;
151//! }
152//!
153//! .rounded-md {
154//!   border-radius: 0.375rem;
155//! }
156//!
157//! .bg-red-200 {
158//!   background-color: oklch(88.5% .062 18.334);
159//! }"));
160//! ```
161//!
162//! ### What to do next
163//!
164//! 1. The documentation about the composition of a class (also named selector) is [here](crate::selector).
165//! 2. The documentation about all utility classes (handled by plugins) is [here](crate::plugins).
166//! 3. The documentation about the reset CSS commonly used (also named preflight) is [here](crate::preflight).
167//!
168//! ## Command line interface
169//!
170//! A command line interface is also available. Install it using:
171//!
172//! ```bash
173//! cargo install encre-css-cli
174//! ```
175//!
176//! Then run `encrecss --help` for instructions on how to use it.
177#![doc(html_logo_url = "https://gitlab.com/encre-org/encre-css/raw/main/.assets/logo.png")]
178#![forbid(unsafe_code)]
179#![warn(
180    missing_docs,
181    missing_debug_implementations,
182    trivial_casts,
183    trivial_numeric_casts,
184    unstable_features,
185    unused_import_braces,
186    unused_qualifications,
187    unreachable_pub,
188    rustdoc::private_doc_tests,
189    rustdoc::broken_intra_doc_links,
190    rustdoc::private_intra_doc_links,
191    clippy::unnecessary_wraps,
192    clippy::too_many_lines,
193    clippy::string_to_string,
194    clippy::explicit_iter_loop,
195    clippy::unnecessary_cast,
196    clippy::missing_errors_doc,
197    clippy::pedantic,
198    clippy::clone_on_ref_ptr,
199    clippy::non_ascii_literal,
200    clippy::dbg_macro,
201    clippy::map_err_ignore,
202    clippy::use_debug,
203    clippy::map_err_ignore,
204    clippy::useless_let_if_seq,
205    clippy::verbose_file_reads,
206    clippy::panic,
207    clippy::unimplemented,
208    clippy::todo
209)]
210#![allow(clippy::module_name_repetitions, clippy::must_use_candidate)]
211
212pub mod config;
213pub mod error;
214pub mod generator;
215pub mod plugins;
216pub mod preflight;
217pub mod scanner;
218pub mod selector;
219pub mod utils;
220
221#[doc(inline)]
222pub use config::Config;
223
224#[doc(inline)]
225pub use error::{Error, Result};
226
227#[doc(inline)]
228pub use generator::generate;
229
230#[doc(inline)]
231pub use preflight::Preflight;
232
233#[doc(inline)]
234pub use scanner::Scanner;
235
236pub use toml::toml;
237
238/// Various helper preludes.
239pub mod prelude {
240    /// Prelude including all necessary structures, functions and modules used to build a new plugin.
241    ///
242    /// It should be included using:
243    ///
244    /// ```
245    /// use encre_css::prelude::build_plugin::*;
246    /// ```
247    pub mod build_plugin {
248        pub use crate::{
249            generator::{generate_at_rules, generate_class, generate_wrapper},
250            generator::{ContextCanHandle, ContextHandle},
251            plugins::Plugin,
252            selector::Modifier,
253            utils::{buffer::Buffer, color, format_negative, shadow, spacing, value_matchers::*},
254        };
255        pub use std::fmt::{self, Write};
256    }
257}