This guide provides simple, practical examples to get you started with tailwind-rs.
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Node.js and PNPM (for Playwright tests)
curl -fsSL https://get.pnpm.io/install.sh | sh
pnpm installcargo new my-tailwind-app
cd my-tailwind-appCargo.toml
[package]
name = "my-tailwind-app"
version = "0.1.0"
edition = "2021"
[dependencies]
leptos = "0.6"
tailwind-rs = "0.1.0"
tailwind-rs-leptos = "0.1.0"
[build-dependencies]
tailwind-rs-cli = "0.1.0"build.rs
fn main() {
tailwind_rs_cli::build()
.input("src/**/*.rs")
.output("dist/styles.css")
.watch(true)
.run()
.expect("Failed to build Tailwind CSS");
}tailwind.toml
[build]
input = ["src/**/*.rs"]
output = "dist/styles.css"
watch = true
[theme]
[theme.colors]
primary = "#3b82f6"
secondary = "#64748b"src/components/button.rs
use leptos::*;
use tailwind_rs::classes;
#[component]
pub fn Button(children: Children) -> impl IntoView {
let button_classes = classes!(
"bg-blue-500",
"hover:bg-blue-700",
"text-white",
"font-bold",
"py-2",
"px-4",
"rounded"
);
view! {
<button class=button_classes>
{children()}
</button>
}
}src/components/card.rs
use leptos::*;
use tailwind_rs::classes;
#[component]
pub fn Card(children: Children) -> impl IntoView {
let card_classes = classes!(
"bg-white",
"shadow-lg",
"rounded-lg",
"p-6",
"max-w-sm"
);
view! {
<div class=card_classes>
{children()}
</div>
}
}
#[component]
pub fn CardTitle(children: Children) -> impl IntoView {
let title_classes = classes!(
"text-xl",
"font-bold",
"text-gray-800",
"mb-4"
);
view! {
<h2 class=title_classes>
{children()}
</h2>
}
}
#[component]
pub fn CardContent(children: Children) -> impl IntoView {
let content_classes = classes!(
"text-gray-600",
"leading-relaxed"
);
view! {
<div class=content_classes>
{children()}
</div>
}
}src/components/input.rs
use leptos::*;
use tailwind_rs::classes;
#[component]
pub fn Input(
#[prop(optional)] placeholder: Option<String>,
#[prop(optional)] input_type: Option<String>,
) -> impl IntoView {
let input_classes = classes!(
"w-full",
"px-3",
"py-2",
"border",
"border-gray-300",
"rounded-md",
"focus:outline-none",
"focus:ring-2",
"focus:ring-blue-500",
"focus:border-transparent"
);
view! {
<input
class=input_classes
type=input_type.unwrap_or_else(|| "text".to_string())
placeholder=placeholder.unwrap_or_else(|| "".to_string())
/>
}
}src/main.rs
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use axum::{
routing::get,
Router,
};
use tower_http::services::ServeFile;
mod components;
use components::{Button, Card, CardTitle, CardContent, Input};
#[component]
pub fn App() -> impl IntoView {
view! {
<div class="min-h-screen bg-gray-100 py-8">
<div class="container mx-auto px-4">
<h1 class="text-3xl font-bold text-center mb-8 text-gray-800">
"Welcome to tailwind-rs"
</h1>
<div class="max-w-md mx-auto">
<Card>
<CardTitle>"Basic Example"</CardTitle>
<CardContent>
<p class="mb-4">
"This is a simple example of tailwind-rs in action."
</p>
<div class="space-y-4">
<Input
placeholder="Enter your name"
input_type="text"
/>
<Button>
"Click me!"
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
}
}
#[tokio::main]
async fn main() {
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(App);
let app = Router::new()
.leptos_routes(&leptos_options, routes, App)
.fallback(ServeFile::new("dist/styles.css"))
.with_state(leptos_options);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
println!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}src/components/button.rs (continued)
#[cfg(test)]
mod tests {
use super::*;
use tailwind_rs::testing::*;
#[test]
fn test_button_classes() {
let button = Button(|| view! { "Test" });
let classes = extract_classes(button);
assert!(classes.contains("bg-blue-500"));
assert!(classes.contains("hover:bg-blue-700"));
assert!(classes.contains("text-white"));
assert!(classes.contains("font-bold"));
assert!(classes.contains("py-2"));
assert!(classes.contains("px-4"));
assert!(classes.contains("rounded"));
}
}tests/integration_tests.rs
use my_tailwind_app::*;
use tailwind_rs::testing::*;
#[test]
fn test_app_rendering() {
let app = create_test_app(|| view! { <App /> });
let html = render_to_string(app);
assert!(html.contains("Welcome to tailwind-rs"));
assert!(html.contains("bg-gray-100"));
assert!(html.contains("container mx-auto"));
}
#[test]
fn test_card_component() {
let card = Card(|| view! { "Test content" });
let html = render_to_string(card);
assert!(html.contains("bg-white"));
assert!(html.contains("shadow-lg"));
assert!(html.contains("rounded-lg"));
assert!(html.contains("p-6"));
}tests/basic-usage.spec.ts
import { test, expect } from '@playwright/test';
test('basic usage example renders correctly', async ({ page }) => {
await page.goto('/');
// Check page title
const title = page.locator('h1');
await expect(title).toHaveText('Welcome to tailwind-rs');
// Check card component
const card = page.locator('.bg-white.shadow-lg.rounded-lg');
await expect(card).toBeVisible();
// Check button component
const button = page.locator('button');
await expect(button).toBeVisible();
await expect(button).toHaveText('Click me!');
// Check input component
const input = page.locator('input[type="text"]');
await expect(input).toBeVisible();
await expect(input).toHaveAttribute('placeholder', 'Enter your name');
});
test('button hover state works', async ({ page }) => {
await page.goto('/');
const button = page.locator('button');
await button.hover();
const styles = await button.evaluate((el) => {
const computed = getComputedStyle(el);
return {
backgroundColor: computed.backgroundColor
};
});
expect(styles.backgroundColor).toBe('rgb(37, 99, 235)'); // blue-700
});
test('input focus state works', async ({ page }) => {
await page.goto('/');
const input = page.locator('input[type="text"]');
await input.focus();
const styles = await input.evaluate((el) => {
const computed = getComputedStyle(el);
return {
outline: computed.outline,
boxShadow: computed.boxShadow
};
});
expect(styles.outline).toBe('none');
expect(styles.boxShadow).toContain('rgb(59, 130, 246)'); // blue-500 ring
});# Start the development server
cargo run
# In another terminal, start the CSS watcher
cargo run --bin tailwind-watch# Build the application
cargo build --release
# Build CSS
cargo run --bin tailwind-build# Run unit tests
cargo test
# Run integration tests
cargo test --test integration_tests
# Run Playwright tests
pnpm test:e2euse tailwind_rs::classes;
let button_classes = classes!(
"bg-blue-500",
"hover:bg-blue-700",
"text-white"
);<Card>
<CardTitle>"Title"</CardTitle>
<CardContent>"Content"</CardContent>
</Card>let classes = if is_active {
classes!("bg-blue-500", "text-white")
} else {
classes!("bg-gray-200", "text-gray-700")
};use tailwind_rs::responsive;
let responsive_classes = classes!(
"grid",
"grid-cols-1",
responsive::md("grid-cols-2"),
responsive::lg("grid-cols-3")
);- 🎨 Explore Button Components
- 🧪 Learn Testing Patterns
- 🎯 Try Framework-Specific Examples
- 🏗️ Build Real-World Applications