AsheeUI was built with AI.

How it was built is not documented here yet.

Signal: signalAshee Softworks

Design Philosophy

AsheeUI abstracts the HTML element and the utility classes, so a component copied into a project works the way it did where it was copied from.

conceptMarkdown

AsheeUI exists so that a component can be written once and used everywhere: copied out of the documentation into a project, imported from the package, or written by the CLI's template. That promise holds only if a component owns two things, which is why the framework abstracts both.


The idea

A component brings its own element and its own styling. You write <Button>, and the component decides whether that is a <button> or an <a>, which variant and size it carries, and what a colour role resolves to. You do not write the element, and you do not write the classes.

Everything on this page is a consequence of that:

  • The element is the component's decision, because one call has to render a control in one place and a navigation in another.
  • The styling is the theme's decision, because a copied component has to arrive looking right in a project it has never seen.
  • The layout is a component too, because needing a wrapper is the most common reason a project starts writing markup by hand.

What is abstracted

The HTML element. Button renders a <button>, or an <a> when it is given an href or a link. Card renders a div, or the link component you name. Link renders an anchor, or your router's link. The element follows what the component was asked to do, and it is written once, inside the framework.

The utility classes. A variant, a size, a colour role, a radius and a spacing step are props rather than class strings. <Badge color="primary" variant="faded"> is the whole styling decision: the component resolves the pair to one static class string that the theme produced. Nothing in your application has to name a Tailwind utility to make an AsheeUI component look the way it is supposed to look.

The layout. Container, Section, Grid, Stack, HStack, VStack and Centered exist so that composition does not fall back to markup with classes. A band of a page is a Section with a spacing step, not a div with padding utilities.


How it resolves

Every option a component takes resolves through one cascade, in the same order:

  1. the prop on the component, such as color="danger"
  2. the component's configuration, such as components.badge.color in asheeui.config.ts
  3. the application's defaults on the provider, such as defaultRadius
  4. the framework's fallback

Because the styling is resolved rather than written, the same copied component renders correctly in a project whose theme is not the theme it was copied from: the component asks the theme what primary is there, and the theme answers.


Your router and image components still work

Abstraction is not isolation. A link still has to be your router's link and an image still has to be your application framework's image, or the application gives up prefetching, the image pipeline and everything else that framework is for.

So the components that render one take a substitution:

tsx
import { Link } from "asheeui";
import NextLink from "next/link";
export function DashboardLink() {
return (
<Link href="/dashboard" component={NextLink} componentProps={{ prefetch: true }}>
Dashboard
</Link>
);
}

component names the component to render in place of the native element, and componentProps carries what it needs. What you name renders inside the AsheeUI component, and that is the whole point: the router keeps the navigation, the image component keeps the loading, and AsheeUI keeps the styling, the states and the accessibility. The same pair works on Image for a framework's image component and on Form for its form component, and the components that render a link or an image themselves take it as well: Card, Button, Avatar, Breadcrumb, Navbar, Footer and Testimonials.

This is the strongest argument for writing the component instead of the element. An <a href> written by hand cannot become a router link later; a <Link href> can.


In practice

  • The CLI writes an application whose provider, styles and configuration are already in place.
  • A component is copied out of these pages, or imported from the package.
  • The theme decides what its colours, radii and spacing resolve to.
  • The framework's routing and images arrive through substitution, so the application keeps its own navigation and its own image pipeline.

What to avoid

Hand-written markup and utility classes for something the framework provides. It is the one habit that works against the reason to use the framework at all:

  • What you write is not in the theme, so it cannot follow a change to the theme.
  • It is not part of the cascade, so a project default cannot reach it.
  • It does not travel. A copied file that leans on utilities you added reads differently where those utilities are not in use, or are overridden.
  • It cannot take a substitution. Framework components arrive through component and componentProps, which is how your router's link and your application framework's image get in. An anchor written by hand has nowhere to put them, so it stays a plain anchor while the rest of the application navigates through the router and loads images through the framework's pipeline.
  • It becomes a second source of truth for something the component already decides.

There is an escape hatch, and it is deliberate: every component accepts className, because the framework is Tailwind-native and a genuine one-off is a real thing. The recommendation is to reach for it last, after a prop and a token and the configuration have each been considered, and never as the way an application's screens are built.

When something needs to exist in its own right, build it from AsheeUI components and give it a name. That is a component which travels, inherits the theme and takes a substitution, which is the same standard the framework holds itself to.