Curvomorphism
Curvomorphism
Curvomorphism is a directional corner-rounding system that makes corners round toward a center point and sharp toward edges, creating a visual “pull” toward the content center.
The Concept
Traditional UI gives all corners the same radius: every card looks identical regardless of position.
Curvomorphism makes corners directional:
- Corners facing the center of the layout → rounded
- Corners facing the edges of the viewport → sharp
This creates an organic visual flow that guides the user’s eye toward the content center.
Layout with center point (C):
┌─────────┐ ┌─────────┐ ┌─────────┐
│ TL │ │ T │ │ TR │
│ │ │ │ │ │
└─────────╯ ╰────────╯ ╰─────────┘
Bottom-right Bottom L+R Bottom-left
rounded rounded rounded
┌─────────╮ ╭─────────┐
│ L │ ←──── (C) ────→ │ R │
│ │ │ │
└─────────╯ ╰─────────┘
Right-side Left-side
rounded rounded
┌─────────╮ ╭─────────╮ ╭─────────┐
│ BL │ │ B │ │ BR │
│ │ │ │ │ │
└─────────┘ └─────────┘ └─────────┘
Top-right Top L+R Top-left
rounded rounded rounded
The Algorithm
For each element, the algorithm:
- Gets the element’s bounding rect and computes its center point
- Compares the element center to the layout center
- For each corner, checks if both edges meeting at that corner face inward (toward center)
- Rounds corners that face inward, keeps others sharp
function applyCornerRounding(element, centerX, centerY, radiusValue) {
const rect = element.getBoundingClientRect();
const elCenterX = rect.left + rect.width / 2;
const elCenterY = rect.top + rect.height / 2;
const r = radiusValue; // rounded
const s = "0px"; // sharp
// Which edges face toward center?
const leftIn = elCenterX > centerX;
const rightIn = elCenterX < centerX;
const topIn = elCenterY > centerY;
const bottomIn = elCenterY < centerY;
// Round only corners where BOTH meeting edges face inward
element.style.borderTopLeftRadius = topIn && leftIn ? r : s;
element.style.borderTopRightRadius = topIn && rightIn ? r : s;
element.style.borderBottomLeftRadius = bottomIn && leftIn ? r : s;
element.style.borderBottomRightRadius = bottomIn && rightIn ? r : s;
}
Usage in Sakko
Basic
<card(curved) {
text: "Auto-curvomorphic card"
}>
With Radius Control
card(curved soft) // 4px radius
card(curved medium) // 8px radius (default)
card(curved strong) // 12px radius
Explicitly Flat
<card(flat) {
text: "No curvomorphism"
}>
API
applyCurvomorphism(element, options?)
Apply curvomorphism to a single element.
import { applyCurvomorphism } from '@nisoku/sazami';
applyCurvomorphism(myElement, {
radius: 'strong', // Token name: 'soft', 'medium', 'strong'
centerX: 500, // Custom center X (default: viewport center)
centerY: 400, // Custom center Y (default: viewport center)
});
Options:
| Option | Type | Default | Description |
|---|---|---|---|
radius |
string |
"medium" |
Radius token name |
centerX |
number |
viewport center | Layout center X coordinate |
centerY |
number |
viewport center | Layout center Y coordinate |
enableCurvomorphism(element, options?)
Conditionally apply curvomorphism if the element has a curved attribute. Reads the radius attribute from the element.
import { enableCurvomorphism } from '@nisoku/sazami';
// In a Web Component's connectedCallback:
connectedCallback() {
this.render();
enableCurvomorphism(this);
}
Behavior
- Curvomorphism is applied after the element is rendered (via
requestAnimationFrame) - A
resizeevent listener automatically re-applies rounding when the viewport changes - The radius value is resolved from CSS variables (
--saz-radius-{type}) with built-in fallbacks - By default, the center point is the viewport center (
window.innerWidth/2,window.innerHeight/2)
When to Use
Good for:
- Cards in grids
- Sidebars and drawers
- Modal dialogs
- Floating toolbars
- Navigation panels
- Media players
Skip for:
- Tiny buttons (too small to notice)
- Dense control panels (looks noisy)
- Full-bleed backgrounds
- Text-only elements