Configuration
AgufaUI configuration is mainly for customizing components.
Shorthand Properties
There are a few common properties in AgufaUI components, we use shorthand to avoid repetitive typing, save time and energy:
tstands for "type" - component type defined in configuration. Every component hastproperty.cstands for "class" - css classes apply to root html element of a component. For "basic" category components, it usually is the main html element itself, likeabutton,capplies to<button>element, but forainput, it's actually<div>container element. For others, it usually is the container<div>or<span>element. Every component hascproperty.vstands for "value" - main data input of a component. For user input html elements, it's the input value; for content display html elements, it's the content to be displayed.vcstands for "value class" - css classes apply to html element that containsvvalue. For user input html elements, it's the element itself; for content display html elements, it's usually<div>or<span>elements.istands for "icon"icstands for "icon class" - css classes apply to<div>or<span>element that contains icon.- Property name ends with character
c- css classes apply to html element that contains that property. Like "loadc" means "load class", apply to<div>or<span>element that contains loading icon.
There will be different *c properties for different elements of a component.
Abutton Elements
Abutton has 4 html elements,
cproperty for<button>vcproperty for text<span>icproperty for icon<div>loadcproperty for loading icon<div>
You can apply different classes to different elements. Hover and click on below button to see effect
<abutton
t="focus" // component type "focus"
v="Hello" // display text "hello"
vc="animate-pulse" // css class for html element that contains "hello"
c="animate-spin text-white bg-blue-6 hover:animate-fade-in hover:bg-blue-7 focus:animate-fade-out focus:ring-blue-5" // css class for button html element
i="i-ph-anchor" // icon
ic="animate-spin text-green-3" // css class for html element that contains icon
/>
Customization
There are two ways to customize components:
Component Instance Level
<abutton v="Hello World" c="text-red hover:bg-blue-2" />
Component Level through Configuration
Config object is made available to AgufaUI through provide/inject pattern or global variable if your framework doesn't support provide/inject.
IUserConfig object has property "theme", which is used for customizing component.
// structure
theme: {
[component name]: {
[component type name]: {
[property name]: [property value]
...
}
...
}
...
}
Click to see IUserConfig definition
import type { TLang } from "@agufaui/locale";
export const CDefaultType = "default";
export const CUseType = "useType";
export const CBase = "base:";
/**
* @example "hello" | true | undefined
*/
export type TFieldValue = string | boolean | undefined;
/**
* @example { text: "hello", show: true }
*/
export type TComponentType = Record<string, any>;
/**
* @example { "default": { text: "hello", show: true }, "primary": { text: "world", show: false } }
*/
export type TComponent = Record<string, TComponentType>;
/**
* @summary The theme object.
*/
export interface ITheme {
[componentName: string]: TComponent;
}
/**
* @summary The user configuration object.
*/
export interface IUserConfig {
/**
* ISO 639-1 code
* @type {string|object} string or Ref<string> for vue or Writable<string> for svelte
* @default "en"
*/
locale?: string | object;
/**
* Language objects
* @type {TLang[]} Locale objects imported from language files
* @default [en] import { en } from "@agufaui/locale";
*/
locales?: TLang[];
/**
* Base theme
* @default undefined
*/
baseTheme?: ITheme;
/**
* User configured Theme. If base theme is provided, will be merged.
* @default undefined
*/
theme?: ITheme;
}
Theme
Type: ITheme
Default: undefined
- You can find [component name] under "Usage" section of each component page.
- [component type name] is user defined.
- You should provide at least a type called default for each component.
- default type will be applied when no
tproperty is specified for that component. - You can specify
useType: ${type_name}in each component type declaration to use that type as base to merge. AgufaUI will look for that type in current theme component scope, then in baseTheme component scope if you are using custom theme. - If you are using custom theme as baseTheme, you can specify
useType: base:${type_name}to use base type directly. - Configuration values will only apply if property is undefined, aka user didn't specify that property (attribute) in html tag.
- There is no need to configure boolean type properties, because javascript will default boolean type properties to false.
tproperty is not configurable. For properties that are not configurable, you'll see "Not configurable" in comment for that property in type definition of component.- You can split Theme to smaller files then import and combine them. For example, abutton.ts, aalert.ts, etc.. For Typescript, you'll need to
import { TComponent } from '@agufaui/config', then do a Type Assertiontheme: { abutton: AButtonConfig as TComponent }
Here's an example, suppose you are using AgufaUI provided theme as baseTheme:
/**
* CDefaultType is just string constant "default"
* CUseType is just string constant "useType"
* CBase is just string constant "base:"
*
* If you're using Vue, you can import from '@agufaui/vue'
*/
import { IUserConfig, Theme, CDefaultType, CUseType, CBase } from "@agufaui/config";
const userConfig: IUserConfig = {
// custom theme from 3rd party
baseTheme: Theme,
// user defined theme
theme: {
abutton: {
[CDefaultType]: {
// user defined default type, replace default type in baseTheme
[CUseType]: CDefaultType, // merge with default type in baseTheme
c: "bg-red-5 hover:bg-red-6 hover:text-white",
loadc: "text-green",
},
blue: {
// user defined blue type
[CUseType]: CBase + CDefaultType, // merge with default type in baseTheme
c: "bg-red-5 hover:bg-red-6 hover:text-white",
loadc: "text-blue",
},
green: {
// user defined green type
[CUseType]: CDefaultType, // merge with above user defined default type
c: "text-white",
},
yellow: {
// user defined yellow type
[CUseType]: "green", // merge with above user defined green type
c: "text-white",
},
},
},
};
Merge Rules
- User defined theme component type will override same component type that exits in baseTheme directly without merging.
- To merge with baseTheme or other type, you need to define
useType:
theme: {
abutton: {
default: {
useType: "default", // or "base:default"
c: "bg-red-5 hover:bg-red-6",
loadc: "text-green",
},
}
}
- Only one
useTypevalue can be defined. useTypecomponent type properties will fall through to calling type:
{
baseTheme: {
abutton: {
default: {
loadc: "text-white",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
c: "text-green",
},
}
}
}
will become
theme: {
abutton: {
default: {
loadc: "text-white",
c: "text-green",
},
}
}
- Duplicate properties with name ends with character "c" (class) will be concatenated:
{
baseTheme: {
abutton: {
default: {
loadc: "text-red",
c: "text-white",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
loadc: "text-red",
c: "text-green",
},
}
}
}
will become
theme: {
abutton: {
default: {
loadc: "text-red text-red",
c: "text-white text-green",
},
}
}
Simple concatenation of classnames is chosen over replacement for performance reasons. It's up to you to avoid duplicate classnames like above example
loadc: "text-red text-red"or duplicate utility specificity classnames likeloadc: "text-green text-red".
- Duplicate properties with name not ends with character "c" (not class) will be overrided:
{
baseTheme: {
abutton: {
default: {
spacex: "space-x-1.5",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
spacex: "space-x-1.2",
},
}
}
}
will become
theme: {
abutton: {
default: {
spacex: "space-x-1.2",
},
}
}
- Replace Field Operator
rf@will replace parent field directly without merging. Putrf@at beginning followed by a space.
{
baseTheme: {
abutton: {
default: {
c: "text-blue text-lg",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
c: "rf@ space-x-1.2",
},
}
}
}
will become
theme: {
abutton: {
default: {
c: "space-x-1.2",
},
}
}
- Delete Field Operator
df@will delete parent field directly
{
baseTheme: {
abutton: {
default: {
c: "text-blue text-lg",
vc: "bg-red",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
c: "df@",
},
}
}
}
will become
theme: {
abutton: {
default: {
vc: "bg-red",
},
}
}
- Replace Classname Operator
r@only applies to a single classname that immediately follows it. It will greedily replace matching utility (eg. text, bg) classnames in parent field, ignoring variants (eg. blue-500, sm, md, lg)
{
baseTheme: {
abutton: {
default: {
c: "text-blue text-lg bg-red",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
c: "r@ text-white text-sm bg-blue!",
},
}
}
}
will become
theme: {
abutton: {
default: {
c: "bg-red text-white text-sm bg-blue!",
},
}
}
r@ will apply to text-white, note that a space is required between r@ and text-white because without this space, your utility css library compiler will not recognize r@text-white, thus text-white will not be generated to css file.
- Delete Classname Operator
d@only applies to a single classname. It will only delete matching classname in parent field
{
baseTheme: {
abutton: {
default: {
c: "text-blue text-lg bg-red",
},
},
},
theme: {
abutton: {
default: {
useType: "default",
c: "d@text-blue text-white",
},
}
}
}
will become
theme: {
abutton: {
default: {
c: "text-lg bg-red text-white",
},
}
}
d@ will apply to text-blue, note that no space is required between d@ and text-blue because your utility css library compiler already picked up text-blue defined in baseTheme.
baseTheme
Type: ITheme
Default: undefined
Specify custom theme you want to use. If baseTheme is not set, you are programming with unstyled components with only positional and browser reset CSS.
- AgufaUI provides a minimum style theme.
Vue
import { Theme, CConfigProvideName } from '@agufaui/vue';
import '@agufaui/vue/theme.css';
app.provide<Config>(CConfigProvideName, new Config({
baseTheme: Theme,
theme: {...},
}));
Svelte
import { Theme } from '@agufaui/theme';
import '@agufaui/svelte/theme.css';
configStore.set(
new Config({
baseTheme: Theme,
theme: {...},
})
);
- If you don't want to use whole AgufaUI provided theme, but just want to use specific component or component type
Vue
import { CConfigProvideName, DAAlert, DAButtonCircle } from '@agufaui/vue';
import '@agufaui/vue/theme.css';
app.provide<Config>(CConfigProvideName, new Config({
theme: {
abutton: {
circle: DAButtonCircle,
...
},
aalert: DAAlert,
...
},
}));
Svelte
import { DAAlert, DAButtonCircle } from '@agufaui/theme';
import '@agufaui/svelte/theme.css';
configStore.set(
new Config({
theme: {
abutton: {
circle: DAButtonCircle,
...
},
aalert: DAAlert,
...
},
})
);
- If you are programming with unstyled components, you need to specify child components types for composed components.
For example, AaLertError has child component Aalert. In AgufaUI provided theme, AalertError default type is:
theme: {
aalertError: {
default: {
errorAAlertType: "red",
successAAlertType: "green",
}
}
}
"red" is Aalert red type, and "green" is Aalert green type in AgufaUI provided theme:
theme: {
aalert: {
red: { ... },
green: { ... },
}
}
locale
Type: string | object (string or Ref<string> for Vue or Writable<string> for Svelte)
Default: "en"
ISO 639-1 Codes. If you only need 1 language, just specify string value. If you need more than 1 language, need to make field reactive. For Vue, locale: ref<string>("en"); for Svelte, locale: writable<string>("en"). For usage, see I18n.
locales
Type: TLang[] (TLang type is imported from @agufaui/vue for Vue or @agufaui/locale for Svelte)
Default: [en] (en TLang is imported from @agufaui/vue for Vue or @agufaui/locale for Svelte)
locales field is required when you need more than 1 language or want to use another language other than en as default. Manually import and input language files is required because it doesn't make sense to bundle all 109 language files. For usage, see I18n.
Component Instance Level Customization with Configuration defined
- For duplicate component properties with name ends with character "c" (class), values will be concatenated:
// configuration without baseTheme
new Config({
theme: {
abutton: {
default: {
c: "text-white bg-blue-5 hover:bg-blue-6",
},
},
},
});
<abutton v="Hello World" c="focus:ring-blue-4" />
will compile to
<button class="text-white bg-blue-5 hover:bg-blue-6 focus:ring-blue-4">Hello World</button>
- For duplicate component properties with name not ends with character "c" (not class), values will be overrided:
// configuration without baseTheme
new Config({
theme: {
abutton: {
default: {
spacex: "space-x-1.5",
},
},
},
});
<abutton v="Hello World" i="i-ph-anchor" spacex="space-x-1.2" />
will compile to
<button ...>
<div class="... space-x-1.2">...</div>
</button>
- For non-duplicate component properties, both component instance level and component configuration level values will apply.
Disable configuration value for property names not ends with character "c" (not class)
To disable configuration value for a property with name not ends with character "c", simply assign its value to empty string
<abutton v="Hello World" spacex="" />
or disable it in configuration
abutton: {
default: {
useType: "default",
spacex: ""
}
}
c Property
- Behaves the same as
classattribute of a html element
<button class=""></button>
is same as
<abutton c=""></abutton>
cis applied last in order, so it will override any overlapping classes for that component. It's useful in very rare cases if you want to override AgufaUI default positional and browser reset CSS. You might need to mark classes as important depending on your CSS framework's order of rules. For example:- Suppose agufaui.css has
cursor-waitrule, if you importagufaui.css, it will be in its own css layer. - You used
cursor-waitsomewhere in your own code. - Then you want to override
cursor-waitwithcursor-pointer, resulting class isclass="cursor-wait cursor-pointer" - If your CSS framework order of rules is Alphabetic, your own code css layer will be
css.cursor-pointer { cursor: pointer; } .cursor-wait { cursor: wait; }because you have used
cursor-waitsomewhere in your code, it will be scanned and included in your own code css layer
5. Nowcursor-waitwill be applied because it comes aftercursor-pointerin generated css code- Suppose agufaui.css has
However, if you don't use cursor-wait in your own code, cursor-pointer will override cursor-wait correctly. Or just simply mark it important class="!cursor-pointer".
If mark it important can't solve your problem, create a new component type in configuration.
- You can use your CSS framework features instead of configuration
For example, if you are using Unocss, you can use Shortcuts or @apply
// unocss.config.ts
export default defineConfig {
shortcuts: {
"btn": "hover:border-2 hover:border-blue"
}
}
or
.btn {
@apply hover:border-2 hover:border-blue;
}
then
<abutton v="Hello" c="btn"></abutton>
Not Recommended
Using shortcuts or @apply is easier at first, but other people will have a hard time reading your code. Even yourself if you get back to it after a while and forgot how you did it.
Attribute Inheritance
If your framework supports Fallthrough Attributes, every attribute or event listener that's applicable to the original html element will also be applicable to corresponding AgufaUI component.
<button width="10px" height="10px" ... />
is same as
<abutton width="10px" height="10px" ... />
These attributes will only fall through to the main element of a component, not applicable to other elements of the component, and can only fall through one level down from parent to child.