# Frontend Component Standards **Version:** 1.0 **Last Updated:** December 2025 **Audience:** Frontend Developers --- ## Overview This document defines mandatory standards for all frontend components across Admin, Store, and Shop frontends. Following these standards ensures consistency, maintainability, and a unified user experience. --- ## Golden Rules 1. **Use Jinja Macros** - Never copy-paste HTML patterns; use shared macros 2. **Alpine.js for Interactivity** - All client-side logic uses Alpine.js 3. **Dark Mode Required** - Every component must support dark mode 4. **Accessibility First** - ARIA labels, keyboard navigation, focus states 5. **Mobile Responsive** - Mobile-first design with Tailwind breakpoints --- ## Jinja Macro System ### Available Macro Files All macros are located in `app/templates/shared/macros/`: | File | Purpose | Key Macros | |------|---------|------------| | `pagination.html` | Table pagination | `pagination()`, `pagination_simple()` | | `alerts.html` | Alerts and toasts | `loading_state()`, `error_state()`, `alert_dynamic()` | | `badges.html` | Status indicators | `badge()`, `status_badge()`, `role_badge()` | | `buttons.html` | Button variants | `btn_primary()`, `btn_secondary()`, `action_button()` | | `forms.html` | Form inputs | `form_input()`, `form_select()`, `form_textarea()` | | `tables.html` | Table components | `table_wrapper()`, `table_header()`, `table_empty_state()` | | `cards.html` | Card layouts | `stat_card()`, `card()`, `info_card()` | | `headers.html` | Page headers | `page_header()`, `page_header_flex()`, `refresh_button()` | | `modals.html` | Modal dialogs | `modal()`, `confirm_modal()`, `job_details_modal()` | | `tabs.html` | Tab navigation | `tabs_nav()`, `tabs_inline()`, `tab_button()` | | `inputs.html` | Specialized inputs | `search_autocomplete()`, `number_stepper()` | ### Macro Usage Rules #### RULE 1: Always Import Before Use ```jinja {# ✅ CORRECT - Import at top of template #} {% from 'shared/macros/tables.html' import table_wrapper, table_header %} {% from 'shared/macros/pagination.html' import pagination %} {% block content %} {% call table_wrapper() %} {{ table_header(['Name', 'Email', 'Status']) }} ... {% endcall %} {{ pagination() }} {% endblock %} {# ❌ WRONG - Inline HTML instead of macro #} {% block content %}
...
{% endblock %} ``` #### RULE 2: Use Macros for Repeated Patterns If you find yourself copying HTML more than once, create or use a macro. ```jinja {# ✅ CORRECT - Using badge macro #} {{ status_badge(status='active') }} {{ status_badge(status='pending') }} {{ status_badge(status='inactive') }} {# ❌ WRONG - Copy-pasting badge HTML #} Active Pending ``` #### RULE 3: Use `{% call %}` for Wrapper Macros Macros that wrap content use the `caller()` pattern: ```jinja {# ✅ CORRECT - Using call block #} {% call table_wrapper() %} {{ table_header(['Column 1', 'Column 2']) }} ... {% endcall %} {% call tabs_nav() %} {{ tab_button('tab1', 'First Tab', icon='home') }} {{ tab_button('tab2', 'Second Tab', icon='cog') }} {% endcall %} ``` #### RULE 4: Prefer Macro Parameters Over Custom CSS ```jinja {# ✅ CORRECT - Using size parameter #} {{ number_stepper(model='qty', size='sm') }} {{ number_stepper(model='qty', size='lg') }} {# ❌ WRONG - Adding custom classes #} {{ number_stepper(model='qty') }} ``` --- ## Component Categories ### 1. Layout Components #### Page Headers ```jinja {% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} {% call page_header_flex(title='Page Title', subtitle='Optional description') %} {{ refresh_button(onclick='refreshData()') }} {% endcall %} ``` #### Cards ```jinja {% from 'shared/macros/cards.html' import stat_card %} {{ stat_card( label='Total Users', value='1,234', icon='user-group', color='purple' ) }} ``` ### 2. Data Display Components #### Tables with Pagination ```jinja {% from 'shared/macros/tables.html' import table_wrapper, table_header, table_empty_state %} {% from 'shared/macros/pagination.html' import pagination %} {% call table_wrapper() %} {{ table_header(['Name', 'Email', 'Status', 'Actions']) }} {% endcall %} {{ table_empty_state( message='No items found', icon='inbox', show_condition='!loading && items.length === 0' ) }} {{ pagination() }} ``` #### Badges ```jinja {% from 'shared/macros/badges.html' import status_badge %} {{ status_badge(status='active') }} {# Green #} {{ status_badge(status='pending') }} {# Yellow #} {{ status_badge(status='inactive') }} {# Red #} {{ status_badge(status='processing') }} {# Blue #} ``` ### 3. Form Components #### Standard Inputs ```jinja {% from 'shared/macros/forms.html' import form_input, form_select %} {{ form_input( name='email', label='Email Address', type='email', model='formData.email', required=true, error_var='errors.email' ) }} {{ form_select( name='status', label='Status', model='formData.status', options=[ {'value': 'active', 'label': 'Active'}, {'value': 'inactive', 'label': 'Inactive'} ] ) }} ``` #### Number Stepper ```jinja {% from 'shared/macros/inputs.html' import number_stepper %} {# Cart quantity #} {{ number_stepper(model='quantity', min=1, max=99, size='md') }} {# Batch size with step #} {{ number_stepper(model='batchSize', min=100, max=5000, step=100, size='lg') }} ``` #### Search Autocomplete ```jinja {% from 'shared/macros/inputs.html' import search_autocomplete, selected_item_display %} {{ search_autocomplete( search_var='searchQuery', results_var='searchResults', show_dropdown_var='showDropdown', loading_var='searching', select_action='selectItem(item)', display_field='name', secondary_field='email', placeholder='Search users...' ) }} {{ selected_item_display( selected_var='selectedUser', display_field='name', secondary_field='email', clear_action='clearSelection()' ) }} ``` ### 4. Navigation Components #### Tabs ```jinja {% from 'shared/macros/tabs.html' import tabs_nav, tabs_inline, tab_button %} {# Full-width navigation tabs #} {% call tabs_nav() %} {{ tab_button('overview', 'Overview', icon='home') }} {{ tab_button('details', 'Details', icon='document') }} {{ tab_button('settings', 'Settings', icon='cog') }} {% endcall %} {# Inline tabs with counts #} {% call tabs_inline() %} {{ tab_button('all', 'All', count_var='items.length') }} {{ tab_button('active', 'Active', count_var='activeCount') }} {{ tab_button('archived', 'Archived', count_var='archivedCount') }} {% endcall %} ``` ### 5. Feedback Components #### Alerts ```jinja {% from 'shared/macros/alerts.html' import alert_dynamic, loading_state, error_state %} {{ alert_dynamic(type='success', message_var='successMessage', show_condition='successMessage') }} {{ alert_dynamic(type='error', message_var='errorMessage', show_condition='errorMessage') }} {{ loading_state(message='Loading data...', show_condition='loading') }} {{ error_state(title='Error', show_condition='error') }} ``` #### Modals ```jinja {% from 'shared/macros/modals.html' import confirm_modal %} {{ confirm_modal( show_var='showDeleteModal', title='Confirm Delete', message='Are you sure you want to delete this item?', confirm_action='deleteItem()', confirm_text='Delete', confirm_class='bg-red-600 hover:bg-red-700' ) }} ``` --- ## Dark Mode Requirements Every component MUST support dark mode using Tailwind's `dark:` prefix. ### Color Pairing Reference | Element | Light Mode | Dark Mode | |---------|------------|-----------| | Background | `bg-white` | `dark:bg-gray-800` | | Card Background | `bg-gray-50` | `dark:bg-gray-900` | | Text Primary | `text-gray-700` | `dark:text-gray-200` | | Text Secondary | `text-gray-500` | `dark:text-gray-400` | | Border | `border-gray-300` | `dark:border-gray-600` | | Hover Background | `hover:bg-gray-100` | `dark:hover:bg-gray-700` | | Input Background | `bg-white` | `dark:bg-gray-700` | | Focus Ring | `focus:ring-purple-500` | `dark:focus:ring-purple-400` | ### Example ```html

Title

Description

``` --- ## Accessibility Requirements ### Required ARIA Attributes ```html {# Button with loading state #} {# Icon-only button #} {# Number stepper group #}
``` ### Focus States All interactive elements must have visible focus states: ```html ``` --- ## Icon System ### Usage ```html {# Standard icon #} {# Icon with color #} {# Inline icon in button #} ``` ### Common Icons Reference | Icon | Usage | |------|-------| | `home` | Dashboard/Home | | `user` | Users/Profile | | `cog` | Settings | | `plus` | Add/Create | | `edit` | Edit/Modify | | `trash` | Delete | | `check-circle` | Success/Verified | | `x-circle` | Error/Failed | | `clock` | Pending/Time | | `refresh` | Reload/Sync | | `eye` | View/Details | | `download` | Download/Export | | `upload` | Upload/Import | | `search` | Search | | `filter` | Filter | | `minus` | Decrease | --- ## Creating New Components ### When to Create a Macro Create a new macro when: - Pattern is used 3+ times across templates - Component has configurable options - Component requires consistent styling ### Macro Template ```jinja {# Component Name ============== Brief description of what the component does. Parameters: - param1: Description (required/optional, default: value) - param2: Description Usage: {{ component_name(param1='value', param2='value') }} #} {% macro component_name( param1, param2='default' ) %}
{# Component HTML #}
{% endmacro %} ``` ### Checklist for New Components - [ ] Supports dark mode (`dark:` variants) - [ ] Has ARIA labels for accessibility - [ ] Documented with JSDoc-style comment - [ ] Added to `/admin/components` page - [ ] Added to this documentation - [ ] Uses existing design tokens (colors, spacing) - [ ] Tested in both light and dark mode - [ ] Works on mobile viewport --- ## Component Reference Page For live examples and copy-paste code, visit: **Admin Panel:** `/admin/components` This page includes: - Interactive demos - Code snippets with copy button - All available macros - Usage examples --- ## Related Documentation - [UI Components Quick Reference](ui-components-quick-reference.md) - [Icons Guide](../../development/icons-guide.md) - [Tailwind CSS Guide](../tailwind-css.md) - [Frontend Overview](../overview.md) - [Architecture Rules](../../development/architecture-rules.md)