Docs/uiv1.0.0-rc.1

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 disabled state with a not-allowed cursor and reduced opacity.
  • Theme-aware fallback styles automatically switch between light and dark backgrounds when ThemeService is available.
  • createButton(props) factory for convenience instantiation without new.

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 onClick prop is not serialised into the rendered HTML. When the button is mounted with mountTo(), a click calls onClick (not while disabled). Server-rendered markup needs its own client-side handler.
  • ThemeService is resolved by @InjectProperty() at construction time. When instantiating Button with new Button(props) (or createButton) outside the DI container, themeService will be undefined and the component will use the light-theme fallback styles.
  • generateStyles() is called synchronously in the constructor. Because AIStyler.generateComponentStyles() returns a Promise, the initial render() call may use empty or stale styles if called immediately after construction. In SSR contexts, call render() after the promise settles.

Pitfalls

  • children is text: it is HTML-escaped, so markup in it is shown, not interpreted.
  • The disabled HTML attribute is added to the <button> element, but the .disabled CSS class is added to its wrapper div. If you add a click handler via data-event="click", the handler will still fire even when the button is disabled because the [data-event] delegation does not check the disabled attribute. Guard your handler against this.
  • Re-rendering Button after a theme change requires calling render() again and replacing the DOM element — the component does not update in place.
From the canonical UDIOT public documentation.Try it in the playground ↗