| Crates.io | dioxus_style |
| lib.rs | dioxus_style |
| version | 0.4.2 |
| created_at | 2025-11-17 07:04:35.337868+00 |
| updated_at | 2026-01-04 14:42:08.013659+00 |
| description | Scoped CSS/SCSS styling system for Dioxus components with compile-time processing and SCSS support |
| homepage | https://github.com/jaiprakash274/dioxus_style |
| repository | https://github.com/jaiprakash274/dioxus_style |
| max_upload_size | |
| id | 1936370 |
| size | 41,615 |
Scoped CSS/SCSS styling for Dioxus components - Write CSS or SCSS that's automatically scoped to your components, preventing style conflicts and maintaining clean, modular code.
div, span) use data attributes for isolation.css/.scss files or write inlineinclude_str!Add to your Cargo.toml:
[dependencies]
dioxus_style = "0.3.0" # SCSS enabled by default
# Or explicitly enable SCSS
dioxus_style = { version = "0.3.0", features = ["scss"] }
# Or use without SCSS (smaller dependencies)
dioxus_style = { version = "0.3.0", default-features = false }
Write modern, maintainable styles with SCSS! All compilation happens at build time.
button.scss:
$primary-color: #3498db;
$padding: 10px 20px;
.btn {
background: $primary-color;
color: white;
padding: $padding;
border-radius: 5px;
&:hover {
background: darken($primary-color, 10%);
transform: scale(1.05);
}
&:active {
transform: scale(0.98);
}
}
Component:
use dioxus::prelude::*;
use dioxus_style::with_css;
#[with_css("button.scss")]
fn Button() -> Element {
rsx! {
button {
"data-scope": "{css}",
class: "{css}_btn",
"Click me!"
}
}
}
use dioxus_style::scoped_style;
fn Badge() -> Element {
let css = scoped_style!("
$danger: #e74c3c;
.badge {
background: $danger;
color: white;
padding: 4px 8px;
&:hover {
background: darken($danger, 10%);
}
}
");
rsx! {
span {
"data-scope": "{css}",
class: "{css}_badge",
"New"
}
}
}
The simplest way - styles are automatically injected:
use dioxus::prelude::*;
use dioxus_style::with_css;
#[with_css("button.css")] // or "button.scss"
fn Button() -> Element {
rsx! {
button {
"data-scope": "{css}",
class: "{css}_btn",
"Click me!"
}
}
}
button.css (or button.scss):
.btn {
background: blue;
color: white;
padding: 10px 20px;
border-radius: 5px;
}
.btn:hover {
background: darkblue;
}
button {
cursor: pointer;
border: none;
}
For more control over when styles are injected:
use dioxus::prelude::*;
use dioxus_style::{scoped_style, inject_styles};
#[component]
fn Card() -> Element {
let css = scoped_style!("card.scss"); // Supports both .css and .scss
rsx! {
style { dangerous_inner_html: "{inject_styles()}" }
div {
"data-scope": "{css}",
class: "{css}_card",
h2 {
"data-scope": "{css}",
class: "{css}_title",
"Hello"
}
p {
"data-scope": "{css}",
class: "{css}_content",
"World"
}
}
}
}
No external file needed:
use dioxus::prelude::*;
use dioxus_style::css;
#[component]
fn Badge() -> Element {
// Plain CSS
let css = css!("background: red; color: white; padding: 4px 8px;");
rsx! {
span {
"data-scope": "{css}",
class: "{css}",
"New"
}
}
}
#[component]
fn ScssCard() -> Element {
// Inline SCSS
let css = css!("
$spacing: 1rem;
background: white;
padding: $spacing;
&:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
");
rsx! {
div {
"data-scope": "{css}",
class: "{css}",
"Hover me"
}
}
}
Alternative syntax for defining styled components:
use dioxus::prelude::*;
use dioxus_style::component_with_css;
component_with_css! {
css: "card.scss", // Supports both .css and .scss
fn Card() -> Element {
rsx! {
div {
"data-scope": "{css}",
class: "{css}_card",
"Content"
}
}
}
}
let css = scoped_style!("button.scss");
// 1. Compiles SCSS to CSS (if .scss file)
// 2. Scopes all selectors
// 3. Generates: "sc_a1b2c3d4"
Input SCSS:
$primary: red;
.btn {
color: $primary;
&:hover {
color: blue;
}
}
div {
margin: 10px;
}
#header {
font-size: 24px;
}
After SCSS Compilation:
.btn { color: red; }
.btn:hover { color: blue; }
div { margin: 10px; }
#header { font-size: 24px; }
After Scoping:
.sc_a1b2c3d4_btn { color: red; }
.sc_a1b2c3d4_btn:hover { color: blue; }
div[data-scope="sc_a1b2c3d4"] { margin: 10px; }
#sc_a1b2c3d4_header { font-size: 24px; }
// Use the scoped class name and data-scope attribute
button {
"data-scope": "{css}",
class: "{css}_btn",
"Click"
}
// Renders: <button data-scope="sc_a1b2c3d4" class="sc_a1b2c3d4_btn">Click</button>
All standard SCSS features are supported:
$primary: #3498db;& for modifiers and pseudo-classes@mixin and @includedarken(), lighten(), rgba(), etc.@import and @use@extend+, -, *, /, %#{$variable}@if, @for, @each, @whileVariables and Operations:
$base-spacing: 1rem;
.container {
padding: $base-spacing * 2;
margin: $base-spacing / 2;
}
Nesting:
.card {
background: white;
.title {
font-size: 1.5rem;
}
.content {
color: #666;
}
}
Mixins:
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.container {
@include flex-center;
}
Parent Selector:
.button {
background: blue;
&:hover {
background: darkblue;
}
&--large {
padding: 1rem 2rem;
}
}
| Selector Type | Input | Output | Usage |
|---|---|---|---|
| Class | .btn |
.sc_xxx_btn |
class: "{css}_btn" |
| ID | #header |
#sc_xxx_header |
id: "{css}_header" |
| Element | div |
div[data-scope="sc_xxx"] |
"data-scope": "{css}" |
| Pseudo-class | .btn:hover |
.sc_xxx_btn:hover |
(automatic) |
| Complex | .card > .title |
.sc_xxx_card > .sc_xxx_title |
(automatic) |
Elements are scoped using data-scope attributes:
// SCSS
$spacing: 20px;
div {
padding: $spacing;
}
span.highlight {
color: yellow;
}
// Component
rsx! {
div {
"data-scope": "{css}",
class: "{css}_container",
span {
"data-scope": "{css}",
class: "{css}_highlight",
"Text"
}
}
}
#[with_css("styles.scss")]
fn MyComponent() -> Element {
// Styles automatically injected - no manual inject_styles() needed
rsx! {
div {
"data-scope": "{css}",
/* your content */
}
}
}
#[component]
fn App() -> Element {
rsx! {
// Inject ALL registered styles once at the root
style { dangerous_inner_html: "{inject_styles()}" }
// Your components
MyComponent {}
AnotherComponent {}
}
}
The library searches for CSS/SCSS files in multiple locations:
scoped_style!("button.scss")
// Searches:
// 1. ./button.scss
// 2. ../button.scss
// 3. ../../button.scss
// 4. src/button.scss
#[with_css("button.scss")] // ✅ SCSS file - automatically compiled
#[with_css("button.css")] // ✅ CSS file - scoped only
All complex selectors are fully supported in both CSS and SCSS:
// SCSS with nesting
.parent {
> .child {
color: blue;
}
}
// Compiles to: .sc_xxx_parent > .sc_xxx_child { color: blue; }
// Adjacent sibling
.card + .card {
margin-top: 20px;
}
// Output: .sc_xxx_card + .sc_xxx_card { margin-top: 20px; }
// Mixed selectors with SCSS
div.container {
> span#label {
font-weight: bold;
}
}
// Output: div[data-scope="sc_xxx"].sc_xxx_container > span[data-scope="sc_xxx"]#sc_xxx_label
In release builds, CSS is automatically minified:
// Debug: Preserves formatting for readability
// Release: Removes whitespace and comments for smaller bundles
Uses xxHash (XXH3) for fast, collision-resistant hashing:
// Hash includes file path + content for uniqueness
// Format: "sc_" + base62(hash)
// Example: "sc_3xK9mP2"
┌───────────────────────────────────┐
│ Your Component (compile time) │
│ scoped_style!("button.scss") │
└──────────────┬────────────────────┘
↓
┌───────────────────────────────────┐
│ SCSS Compiler (if .scss) │
│ • Compile SCSS to CSS │
│ • Variables, nesting, mixins │
│ • Functions and operations │
└──────────────┬────────────────────┘
↓
┌───────────────────────────────────┐
│ Procedural Macro │
│ • Read CSS/SCSS file │
│ • Generate hash (xxHash3) │
│ • Scope selectors: │
│ - .btn → .sc_xxx_btn │
│ - #id → #sc_xxx_id │
│ - div → div[data-scope="..."] │
│ • Minify (release builds) │
└──────────────┬────────────────────┘
↓
┌───────────────────────────────────┐
│ Runtime Registry (lazy_static) │
│ • Store scoped CSS │
│ • Deduplicate by hash │
│ • Preserve insertion order │
└──────────────┬────────────────────┘
↓
┌───────────────────────────────────┐
│ inject_styles() → <style> tag │
│ • Inject into DOM │
│ • All styles in single tag │
└───────────────────────────────────┘
use dioxus::prelude::*;
use dioxus_style::{with_css, inject_styles};
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
rsx! {
style { dangerous_inner_html: "{inject_styles()}" }
Header {}
Main {}
Footer {}
}
}
#[with_css("header.scss")]
fn Header() -> Element {
rsx! {
header {
"data-scope": "{css}",
class: "{css}_header",
h1 {
"data-scope": "{css}",
"My App"
}
}
}
}
#[with_css("main.scss")]
fn Main() -> Element {
rsx! {
main {
"data-scope": "{css}",
class: "{css}_container",
Card { title: "Welcome" }
}
}
}
#[with_css("card.scss")]
fn Card(title: String) -> Element {
rsx! {
div {
"data-scope": "{css}",
class: "{css}_card",
h2 {
"data-scope": "{css}",
class: "{css}_title",
"{title}"
}
}
}
}
header.scss:
$header-bg: #2c3e50;
.header {
background: $header-bg;
padding: 1rem;
h1 {
color: white;
margin: 0;
}
}
.button → .sc_xxx_button#header → #sc_xxx_headerdiv → div[data-scope="sc_xxx"] (requires data-scope attribute):hover, :focus, :active, etc.::before, ::after[type="text"] (passed through, element gets scoped)>, +, ~, space)*Good News: This is a NON-BREAKING release! All v0.2.0 code works without changes.
To Use SCSS:
.css files to .scss (optional)Cargo.toml:
# Before (v0.2.0)
[dependencies]
dioxus_style = "0.2.0"
# After (v0.3.0) - SCSS enabled by default
[dependencies]
dioxus_style = "0.3.0"
# Or without SCSS
dioxus_style = { version = "0.3.0", default-features = false }
Breaking Changes:
Element selectors now require data-scope:
// OLD (v0.1.0)
rsx! { div { class: "{css}_container", "Content" } }
// NEW (v0.2.0+)
rsx! {
div {
"data-scope": "{css}",
class: "{css}_container",
"Content"
}
}
Class selector output format changed:
.sc_xxx.button.sc_xxx_buttonID selector output format changed:
#sc_xxx.header#sc_xxx_headererror: SCSS compilation error in 'button.scss': Undefined variable
Solution: Check that all SCSS variables are defined:
// ❌ Wrong
.button { color: $undefined; }
// ✅ Correct
$primary: blue;
.button { color: $primary; }
error: SCSS support is not enabled
Solution: Enable SCSS in Cargo.toml:
[dependencies]
dioxus_style = { version = "0.3.0", features = ["scss"] }
// ❌ Error: Failed to find file 'button.scss'
scoped_style!("button.scss")
// ✅ Solution: Use relative path from Cargo.toml location
scoped_style!("src/components/button.scss")
// ❌ Forgot to inject styles
#[component]
fn App() -> Element {
rsx! { MyComponent {} }
}
// ✅ Add inject_styles() to root component
#[component]
fn App() -> Element {
rsx! {
style { dangerous_inner_html: "{inject_styles()}" }
MyComponent {}
}
}
// ❌ Missing data-scope attribute
div { class: "{css}_container", "Content" }
// ✅ Add data-scope for element scoping
div {
"data-scope": "{css}",
class: "{css}_container",
"Content"
}
Contributions are welcome! Please feel free to submit a Pull Request. See CONTRIBUTING.md for guidelines.
This project is licensed under either of:
at your option.
Built for the Dioxus framework.
SCSS compilation powered by grass.
See CHANGELOG.md for detailed version history.
Made with ❤️ for the Dioxus community