Button Component
Overview
Button is an AI-styling-capable, theme-aware server-renderable button component. It extends BaseComponent<ButtonProps> and is decorated with @Component({ selector: 'udiot-button' }). It injects ThemeService via @InjectProperty() to adapt its fallback styles to the active theme. An optional AIStyler prop triggers AI-generated CSS at render time.
Key Concepts
- Renders a
<button>element wrapped in a<style>block. - Three variants:
primary,secondary,danger. - Three sizes:
small,medium,large. - Supports
disabledstate with anot-allowedcursor and reduced opacity. - Theme-aware fallback styles automatically switch between light and dark backgrounds when
ThemeServiceis available. createButton(props)factory for convenience instantiation withoutnew.
API Reference
ButtonProps
interface ButtonProps {
children: string; // Button label text (required)
variant?: 'primary' | 'secondary' | 'danger'; // Visual style (default: 'primary')
size?: 'small' | 'medium' | 'large'; // Size modifier (default: 'medium')
disabled?: boolean; // Disable the button (default: false)
onClick?: () => void; // Click handler (client-side only)
aiStyler?: AIStyler; // AI styling bridge; omit to use built-in styles
}
Button
@Component({ selector: 'udiot-button' })
class Button extends BaseComponent<ButtonProps> {
constructor(props: ButtonProps);
render(): string; // Returns the full HTML string including <style>
}
Factory
function createButton(props: ButtonProps): Button;
Basic Usage
import { createButton } from '@tagna/udiot';
const btn = createButton({
children: 'Save Changes',
variant: 'primary',
size: 'medium',
});
console.log(btn.render());
// <style>.button { ... }</style>
// <button class="button button-primary button-medium">Save Changes</button>
Common Patterns
All variants
import { createButton } from '@tagna/udiot';
const primary = createButton({ children: 'Confirm', variant: 'primary' });
const secondary = createButton({ children: 'Cancel', variant: 'secondary' });
const danger = createButton({ children: 'Delete', variant: 'danger' });
Size options
const small = createButton({ children: 'Small', size: 'small' });
const medium = createButton({ children: 'Medium', size: 'medium' });
const large = createButton({ children: 'Large', size: 'large' });
Disabled state
const disabled = createButton({
children: 'Submit',
variant: 'primary',
disabled: true,
});
// Renders with HTML `disabled` attribute and `.disabled` CSS class
With AI styling
import { createButton } from '@tagna/udiot';
import { AIStyler } from '@tagna/udiot';
const styler = new AIStyler(aiClient);
const btn = createButton({
children: 'Get Started',
variant: 'primary',
size: 'large',
aiStyler: styler,
});
// generateStyles() is async; call render() after the component is initialised
// in a context where await is available (e.g. inside an async SSR page)
const html = btn.render();
Embedding in an SSR page
import { createButton } from '@tagna/udiot';
export default {
render(): string {
const saveBtn = createButton({ children: 'Save', variant: 'primary' });
const cancelBtn = createButton({ children: 'Cancel', variant: 'secondary' });
return `
<form>
<fieldset><!-- form fields --></fieldset>
<div class="form-actions">
${cancelBtn.render()}
${saveBtn.render()}
</div>
</form>
`;
},
};
Integration Notes
Button.render()emits a<style>block with scoped class names (button,button-primary,button-secondary,button-danger,button-small,button-large). If multiple buttons are rendered on the same page, the styles are repeated in the HTML. Consider extracting the CSS to a shared stylesheet in production.- The
onClickprop is not serialised into the rendered HTML. When the button is mounted withmountTo(), a click callsonClick(not whiledisabled). Server-rendered markup needs its own client-side handler. ThemeServiceis resolved by@InjectProperty()at construction time. When instantiatingButtonwithnew Button(props)(orcreateButton) outside the DI container,themeServicewill beundefinedand the component will use the light-theme fallback styles.generateStyles()is called synchronously in the constructor. BecauseAIStyler.generateComponentStyles()returns aPromise, the initialrender()call may use empty or stale styles if called immediately after construction. In SSR contexts, callrender()after the promise settles.
Pitfalls
childrenis text: it is HTML-escaped, so markup in it is shown, not interpreted.- The
disabledHTML attribute is added to the<button>element, but the.disabledCSS class is added to its wrapperdiv. If you add a click handler viadata-event="click", the handler will still fire even when the button is disabled because the[data-event]delegation does not check thedisabledattribute. Guard your handler against this. - Re-rendering
Buttonafter a theme change requires callingrender()again and replacing the DOM element — the component does not update in place.
From the canonical UDIOT public documentation.Try it in the playground ↗