Doggo Documentation

repository·main·Indexed 19 days ago

https://github.com/woylie/doggo

A headless UI component collection for Phoenix focused on semantic HTML and accessibility. Doggo provides macros via Doggo.Components to generate components using data attributes and semantic elements for styling and state. It includes components such as Avatar, BottomNavigation, Datetime, and Image, and offers tools for generating Phoenix Storybook stories and PurgeCSS safelists.

Tokens
9.3K
Snippets
28
Records
43
Agent score
62%

What's inside Doggo

  1. Understand Doggo component maturity levels

    main

    Doggo components are categorized into four maturity levels to indicate their stability and readiness for production:

    • Experimental: Early development, unstable APIs, not recommended for production.
    • Developing: Complete semantics, but interactivity might be missing. API may change.
    • Refining: Feature-complete, stable API, full accessibility/keyboard interactivity. Suitable for cautious production use.
    • Stable: Fully developed, tested, stable API, fully interactive, and includes complete storybook modules. Ready for production.
  2. Use Doggo.Components to generate Phoenix components

    main

    To use Doggo, include use Doggo.Components in your core components module (or a dedicated module). Doggo.Components provides macros that generate Phoenix components with built-in support for modifiers, base classes, and custom naming.

    Modifiers and Data Attributes

    Modifiers are defined in the macro and are translated into data-* attributes on the rendered HTML.

    • Standard modifiers: The value is converted to a string and added as a data attribute (e.g., size: "small" becomes data-size="small").
    • Boolean modifiers: If the type is set to :boolean, the attribute is added as a presence-only attribute if true, and omitted if false or omitted.

    Customizing Components

    • base_class: Most components have a default base class matching their name. You can override this with a string or remove it by setting it to nil.
    • name: You can change the name of the generated component (e.g., from build_button to build_alt_button) to allow multiple variants or to match your design system.
    defmodule MyAppWeb.CoreComponents do
      use Doggo.Components
      use Phoenix.Component
    
      # Basic component with modifiers
      build_button(
        modifiers: [
          size: [values: ["normal", "small"], default: "normal"]
        ]
      )
    
      # Boolean modifier example
      build_button(modifiers: [full_width: [type: :boolean]])
    
      # Overriding base class and name
      build_button(
        name: :alt_button,
        base_class: "alt-button",
        modifiers: [size: [values: ["normal", "small"], default: "normal"]]
      )
    end
  3. Start the Doggo Demo Phoenix server

    main

    To run the Doggo Demo application locally, follow these steps to install dependencies and launch the Phoenix server:

    1. Install the required Elixir dependencies using mix deps.get.
    2. Start the Phoenix endpoint using mix phx.server.

    Alternatively, if you want to start the server within an interactive Elixir shell (IEx), use: iex -S mix phx.server

    Once started, the application is accessible at http://localhost:4000.

    mix deps.get
    mix phx.server
  4. Import Doggo Phoenix LiveView Hooks

    main

    Some Doggo components require JavaScript hooks for interactivity. To enable them in your Phoenix application, import the hooks from phoenix-colocated/doggo and include them in your LiveSocket configuration.

    import { hooks as doggoHooks } from "phoenix-colocated/doggo";
    
    const Hooks = { ...doggoHooks };
    
    const liveSocket = new LiveSocket("/live", Socket, {
      // ...
      hooks: Hooks,
    });
  5. Configure image aspect ratios with ratio

    main

    The ratio attribute allows you to specify an aspect ratio for the image frame using a string in the format n:d (e.g., "16:9"). This value is used to set data-numerator and data-denominator attributes on the image frame div.

    Supported values include:

    • nil (default)
    • "1:1"
    • "3:2"
    • "2:3"
    • "4:3"
    • "3:4"
    • "5:4"
    • "4:5"
    • "16:9"
    • "9:16"

    Note: If a ratio is provided that does not follow the n:d format, the component will raise an error: invalid ratio. Expected a ratio in the format n:d, e.g. "16:9", got: ....

  6. Use `title_formatter` for accessible tooltips

    main

    You can use the title_formatter attribute to render a shortened or relative format in the visible text while keeping the full value available in the title attribute (accessible via pointer devices).

    Note: While the title attribute is useful for tooltips, some screen readers may prioritize the datetime attribute instead.

    <%-- Example: Displaying relative time but providing full time in the title --%
    <.time
      value={@time}
      formatter={&relative_time/1}
      title_formatter={&MyApp.Cldr.Time.to_string!/1}
    />
  7. Tree component status and limitations

    main

    The Tree component is currently marked as :experimental (since version 0.6.0).

    Current Limitations: As of the current version, the necessary JavaScript for full accessibility and functionality is missing. The following features are not yet implemented:

    • Expand and collapse nodes
    • Node selection
    • Keyboard navigation (arrow keys)
  8. Use the MenuBar component

    main

    The Doggo.Components.MenuBar component renders a menu bar similar to those found in desktop applications. It is intended for organizing application actions rather than site navigation.

    To use it, wrap your menu buttons and menus within the <.menu_bar> component using the :item slot. You can use the :item slot to provide menu buttons, groups, or visual separators.

    Accessibility Requirements: You must provide either a label or a labelledby attribute to ensure the component is accessible. label sets the aria-label, while labelledby sets aria-labelledby to the ID of an existing element.

    <.menu_bar label="Main">
      <:item>
        <.menu_button controls="actions-menu" id="actions-button">
          Actions
        </.menu_button>
    
        <.menu id="actions-menu" labelledby="actions-button" hidden>
          <:item>
            <.menu_item on_click={JS.push("view-dog-profiles")}>
              View Dog Profiles
            </.menu_item>
          </:item>
          <:item>
            <.menu_item on_click={JS.push("add-dog-profile")}>
              Add Dog Profile
            </.menu_item>
          </:item>
          <:item>
            <.menu_item on_click={JS.push("dog-care-tips")}>
              Dog Care Tips
            </.menu_item>
          </:item>
        </.menu>
      </:item>
      <:item role="separator"></:item>
      <:item>
        <.menu_item on_click={JS.dispatch("myapp:help")}>
          Help
        </.menu_item>
      </:item>
    </.menu_bar>
  9. Use the Steps component for form navigation

    main

    The Doggo.Components.Steps component renders a navigation list for multi-step forms. It supports two primary interaction patterns: using Phoenix.LiveView.JS.patch/1 for URL-based navigation or Phoenix.LiveView.JS.push/2 for sending LiveView events.

    Interaction Patterns

    1. Patch Navigation (URL-based): Use this when each step corresponds to a unique URL path.

    <.steps current_step={0}>
      <:step on_click={JS.patch(to: ~p"/form/step/personal-information")}>
        Profile
      </:step>
      <:step on_click={JS.patch(to: ~p"/form/step/delivery")}>
        Delivery
      </:step>
      <:step on_click={JS.patch(to: ~p"/form/step/confirmation")}>
        Confirmation
      </:step>
    </.steps>

    2. Push Events (LiveView-based): Use this when you want to trigger a specific LiveView event (e.g., go-to-step) with a payload.

    <.steps current_step={0}>
      <:step on_click={JS.push("go-to-step", value: %{step: "profile"})}>
        Profile
      </:step>
      <:step on_click={JS.push("go-to-step", value: %{step: "delivery"})}>
        Delivery
      </:step>
      <:step on_click={JS.push("go-to-step", value: %{step: "confirmation"})}>
        Confirmation
      </:step>
    </.steps>
    <.steps current_step={0}>
      <:step on_click={JS.patch(to: ~p"/form/step/personal-information")}>
        Profile
      </:step>
      <:step on_click={JS.patch(to: ~p"/form/step/delivery")}>
        Delivery
      </:step>
      <:step on_click={JS.patch(to: ~p"/form/step/confirmation")}>
        Confirmation
      </:step>
    </.steps>
  10. Use the Datetime component

    main

    The Doggo.Components.Datetime component formats a DateTime or NaiveDateTime and renders it within a <time> element. It supports custom formatting, precision truncation, timezone shifting, and an optional title attribute for accessibility.

    <%/.datetime value={~U[2023-02-05 12:22:06.003Z]} />
  11. Use the Doggo.Components.Menu component

    main

    The Doggo.Components.Menu component renders a menu for organizing application actions (rather than site navigation). It is designed to work either as a permanently visible menu with a label or as a toggleable menu controlled by a menu_button/1 using controls and labelledby attributes.

    Note: This component is currently marked as :experimental. Full JavaScript functionality for focus management and keyboard support is not yet implemented.

    <%-- Scenario 1: Always visible menu --%>
    <.menu label="Actions">
      <:item>Copy</:item>
      <:item>Paste</:item>
      <:item role="separator"></:item>
      <:item>Sort lines</:item>
    </.menu>
    
    <%-- Scenario 2: Toggleable menu via menu_button --%>
    <.menu_button controls="actions-menu" id="actions-button">
      Actions
    </.menu_button>
    <.menu labelledby="actions-button" hidden></.menu>