Skip to content

Commit a1829ea

Browse files
committed
Update readme
1 parent 161b3de commit a1829ea

File tree

1 file changed

+53
-40
lines changed

1 file changed

+53
-40
lines changed

README.md

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pip install "violetear[server]"
3535

3636
## 🚀 Quickstart: The Isomorphic Counter
3737

38-
Let's build a fully interactive "Counter" app where the state updates instantly in the browser, but every change is reported back to the server. **Zero JavaScript required.**
38+
Let's build a fully interactive "Counter" app where the state updates instantly in the browser using our Pythonic DOM API, but every change is reported back to the server. **Zero JavaScript required.**
3939

4040
### Step 1: Initialize the App
4141

@@ -46,6 +46,7 @@ from violetear import App, StyleSheet
4646
from violetear.markup import Document, Element
4747
from violetear.color import Colors
4848
from violetear.style import Style
49+
from violetear.dom import Event
4950

5051
app = App(title="Violetear Counter")
5152
```
@@ -58,21 +59,25 @@ Instead of writing CSS strings, use the fluent API to define your theme.
5859
# Create a global stylesheet
5960
style = StyleSheet()
6061

61-
# Layout
62-
style.select("body").background(Colors.AliceBlue).font(family="sans-serif") \
63-
.flexbox(align="center", justify="center").height("100vh").margin(0)
62+
style.select("body").background(Colors.AliceBlue).font(family="sans-serif").flexbox(
63+
align="center", justify="center"
64+
).height("320px").margin(top=20)
6465

65-
# Component Styles
66-
style.select(".card").background(Colors.White).padding(40).rounded(15) \
67-
.shadow(blur=20, color="rgba(0,0,0,0.1)").text(align="center")
66+
style.select(".counter-card").background(Colors.White).padding(40).rounded(15).shadow(
67+
blur=20, color="rgba(0,0,0,0.1)"
68+
).text(align="center")
6869

69-
style.select(".count").font(size=64, weight="bold").color(Colors.SlateBlue).margin(10)
70+
style.select(".count-display").font(size=64, weight="bold").color(
71+
Colors.SlateBlue
72+
).margin(10)
7073

71-
style.select("button").padding("10px 20px").font(size=20, weight="bold") \
72-
.margin(5).rounded(8).border(0).rule("cursor", "pointer").color(Colors.White)
74+
style.select("button").padding("10px 20px").font(size=20, weight="bold").margin(
75+
5
76+
).rounded(8).border(0).rule("cursor", "pointer").color(Colors.White)
7377

7478
style.select(".btn-plus").background(Colors.MediumSeaGreen)
7579
style.select(".btn-minus").background(Colors.IndianRed)
80+
style.select(".btn:hover").rule("opacity", "0.8")
7681
```
7782

7883
### Step 3: Server Logic (RPC)
@@ -81,9 +86,9 @@ Define a function that runs on the server. The `@app.server` decorator exposes t
8186

8287
```python
8388
@app.server
84-
def report_count(current_count: int, action: str):
89+
async def report_count(current_count: int, action: str):
8590
"""
86-
This runs on the SERVER.
91+
This runs on the server.
8792
FastAPI automatically validates that current_count is an int.
8893
"""
8994
print(f"[SERVER] Count is now {current_count} (Action: {action})")
@@ -92,30 +97,30 @@ def report_count(current_count: int, action: str):
9297

9398
### Step 4: Client Logic (In-Browser Python)
9499

95-
Define the interactivity. The `@app.client` decorator compiles this function and sends it to the browser to run inside Pyodide.
100+
Define the interactivity. The `@app.client` decorator compiles this function and sends it to the browser to run inside Pyodide. We use `violetear.dom` to manipulate the page using a familiar, pythonic API.
96101

97102
```python
98103
@app.client
99-
async def handle_change(event):
104+
async def handle_change(event: Event):
100105
"""
101-
This runs in the BROWSER.
106+
Runs in the browser.
102107
"""
103-
from js import document
108+
# Import the DOM wrapper (Client-Side implementation)
109+
from violetear.dom import Document
104110

105-
# 1. Interact with the DOM
106-
display_el = document.getElementById("display")
107-
current_value = int(display_el.innerText)
111+
# A. Get current state from DOM
112+
display = Document.find("display")
113+
current_value = int(display.text)
108114

109-
# 2. Update State
110-
action = event.target.id
115+
# B. Determine action
116+
action = event.target.id # "plus" or "minus"
111117
new_value = current_value + (1 if action == "plus" else -1)
112118

113-
# 3. Update UI (Instant feedback)
114-
display_el.innerText = str(new_value)
119+
# C. Update DOM immediately (Responsive)
120+
display.text = str(new_value)
115121

116-
# 4. Sync with Server (Seamless RPC)
117-
# Call our previously defined `report_count` method.
118-
# This looks like a local function call, but it performs a network request!
122+
# D. Sync with Server (Background)
123+
# This calls the @app.server function seamlessly!
119124
await report_count(current_count=new_value, action=action)
120125
```
121126

@@ -132,26 +137,33 @@ def index():
132137
doc.style(style, href="/style.css")
133138

134139
doc.body.add(
135-
Element("div", classes="card").extend(
140+
Element("div", classes="counter-card").extend(
136141
Element("h2", text="Isomorphic Counter"),
137142

138-
# The Counter Display
139-
Element("div", id="display", classes="count", text="0"),
143+
# The Count
144+
Element("div", id="display", classes="count-display", text="0"),
140145

141-
# The Controls
142-
# We attach the Python function directly to the event!
143-
Element("button", id="minus", text="-", classes="btn-minus").on("click", handle_change),
144-
Element("button", id="plus", text="+", classes="btn-plus").on("click", handle_change),
146+
# Controls - Both call the same Python function
147+
Element("button", id="minus", text="-", classes="btn-minus btn").on(
148+
"click", handle_change
149+
),
150+
Element("button", id="plus", text="+", classes="btn-plus btn").on(
151+
"click", handle_change
152+
),
153+
154+
Element("p", text="Check server console for pings.").style(
155+
Style().color(Colors.Gray).margin(top=20)
156+
),
145157
)
146158
)
147-
return doc
148159

160+
return doc
149161

150-
app.run(port=8000)
162+
if __name__ == "__main__":
163+
app.run(port=8000)
151164
```
152165

153-
Run it with `python main.py` and open `http://localhost:8000`. You have a full-stack, styled, interactive app in \~60 lines of pure Python!
154-
166+
Run it with `python main.py` and open `http://localhost:8000`. You have a full-stack, styled, interactive app in just 60 lines of pure Python!
155167

156168
## ✨ Features
157169

@@ -163,7 +175,8 @@ Run it with `python main.py` and open `http://localhost:8000`. You have a full-s
163175
* **Presets**:
164176
* `FlexGrid`: Create complex 12-column layouts with a single line.
165177
* `SemanticDesign`: Pre-configured design systems for typography and buttons.
166-
* `UtilitySystem`: Generate Tailwind-like utility classes (`p-4`, `m-2`, `text-lg`) programmatically.
178+
* `UtilitySystem`: Generate thousands of utility classes (`p-4`, `text-xl`, `flex`, `hover:bg-red-500`) purely in Python without any build step.
179+
* `Atomic`: A pre-made, completely configurable, Tailwind-like atomic CSS based on the utility system.
167180

168181
### 🧱 Component System
169182

@@ -174,6 +187,7 @@ Run it with `python main.py` and open `http://localhost:8000`. You have a full-s
174187
### ⚡ Full-Stack Application Engine
175188

176189
* **Hybrid Architecture**: Supports both **Server-Side Rendering (SSR)** for SEO and speed, and **Client-Side Rendering (CSR)** for interactivity.
190+
* **Pythonic DOM**: A wrapper (`violetear.dom`) that provides a clean, type-safe Python API for DOM manipulation in the browser (`Document.find("id").text("Hello")`).
177191
* **Smart Hydration**: If a page has no interactive elements, `violetear` serves pure HTML. If you add an `@app.client` handler, it automatically injects the runtime.
178192
* **Asset Management**: Stylesheets created in Python are served directly from memory; no need to manage static files manually.
179193
* **Seamless RPC**: Call server functions from the browser as if they were local. Arguments and return values are automatically serialized.
@@ -183,7 +197,6 @@ Run it with `python main.py` and open `http://localhost:8000`. You have a full-s
183197
We are just getting started. Here is what's coming in v1.1 and beyond:
184198

185199
* **📱 Progressive Web Apps (PWA)**: Simply pass `App(pwa=True)` to automatically generate `manifest.json` and a Service Worker, making your Python app installable and offline-capable.
186-
* **🐍 Pythonic DOM**: A wrapper around `js.document` so you can manipulate the browser DOM using pythonic idioms and a fluent interface.
187200
* **🔥 JIT CSS**: An optimization engine that scans your Python code and serves *only* the CSS rules actually used by your components, reducing file size to the minimum.
188201

189202
## 🤝 Contribution
@@ -197,4 +210,4 @@ We are just getting started. Here is what's coming in v1.1 and beyond:
197210

198211
## 📄 License
199212

200-
MIT License. You know what it means!
213+
MIT License. Copyright (c) Alejandro Piad.

0 commit comments

Comments
 (0)