Files
3dealer/CLAUDE.md
T
2026-07-31 17:41:55 +03:00

10 KiB
Raw Blame History

Front-end Guidelines

This is a Laravel 12 e-commerce project using Lunar PHP (headless). These guidelines apply to all front-end work.


Project Language Settings

  • Greek register: singular — use the informal second person (εσύ/σου/σε). e.g. "Η κριτική σου", "Το όνομά σου", "Το email σου"

All UI text written for this project must follow the register above.


Tech Stack

  • Laravel 12 + Lunar PHP 1.3 (headless e-commerce)
  • Blade for templating (@extends, @section, @yield, components)
  • Tailwind CSS v4 via @tailwindcss/vite — zero config, @import 'tailwindcss' only
  • Stimulus JS for interactivity — controllers registered in resources/js/stimulus/index.js
  • Popover API (native browser) for overlays — no JS overlay libraries
  • SQLite database, npm run dev for local asset compilation

Key File Locations

What Where
CSS entry point resources/css/app.css
Font definitions resources/css/fonts.css
JS entry point resources/js/app.js
Stimulus controllers resources/js/stimulus/
JS utilities resources/js/utils/
Layout resources/views/layouts/app.blade.php
UI components (atomic) resources/views/components/ui/
Section components resources/views/components/
Page views resources/views/{page}/
Public assets public/images/

Design Reference

The Bluebeard template (resources/css/bluebeard.css) is used as a reference only for colors, spacing, typography, and effects. It is never imported or used directly. Everything is translated to Tailwind utilities or custom CSS where unavoidable.

The boboko project at /Users/farenoubi/Projects/boboko-test can be referenced for CSS architecture patterns.


Tailwind vs. Custom CSS

Prefer Tailwind utilities by default. Write custom CSS only when something genuinely cannot be expressed as a utility:

  • Pseudo-elements (::before, ::after) with dynamic transforms or transitions
  • Complex descendant/sibling selectors (e.g. .group:hover .nav-dropdown)
  • Keyframe animations
  • The underline-slide animation on nav links (background-size trick)

Never write inline CSS (style="..."). Never write custom CSS for something Tailwind already covers.

When a component requires a CSS class (e.g. as a hook for a pseudo-element), still put all properties that can be Tailwind utilities as classes on the element in the component file. The CSS rule should contain only what genuinely cannot be a utility — pseudo-elements, complex selectors, keyframes. Do not default to putting all styles in the CSS class just because the class exists.

When custom CSS is needed, add it to resources/css/app.css inside @layer components. Keep the rule minimal — only what can't be a utility.


Design Tokens

Defined in @theme {} in app.css:

  • --font-sans: Manrope (body text)
  • --font-display: Manrope (headings, nav, buttons) — kept separate from --font-sans in case they diverge later

Reuse existing colors (neutral-200, black), font sizes, spacing, and border styles as components are built. Do not invent new values — check what's already in use first.


Component Structure

resources/views/components/ui/ — atomic, reusable UI elements

Single-purpose, stateless or minimally stateful elements used across many contexts:

  • button.blade.php ✓
  • input.blade.php
  • input-group.blade.php
  • icon.blade.php
  • image.blade.php
  • tabs.blade.php / tab.blade.php
  • accordion.blade.php
  • tooltip.blade.php
  • badge.blade.php
  • etc.

resources/views/components/ — section-level or composite components

Larger reusable blocks made of several elements, often with real content:

  • header.blade.php ✓
  • footer.blade.php
  • cta.blade.php
  • product-card.blade.php
  • contact-section.blade.php
  • etc.

Interactivity

Popover API for overlays

For tooltips, modals, dropdowns, and any overlay: use the native Popover API with CSS — no JS, no libraries.

<button popovertarget="my-popover">Open</button>
<div id="my-popover" popover>...</div>

Style open/closed states with [popover] and :popover-open selectors in CSS.

Stimulus JS for behavior

Use Stimulus when JS is genuinely needed (form handling, cart updates, dynamic state, etc.).

  • Keep controllers lean: manage state and coordinate the DOM, don't build UI inside them
  • Use Stimulus values, targets, and outlets — avoid reading from the DOM imperatively
  • Never inject HTML strings or classes from inside a controller. Instead, drive appearance via data attributes or CSS classes toggled on existing elements, and let CSS handle the visual result
  • Never write inline JS (onclick="...", etc.)

Pure CSS for simple interactions

Hover dropdowns, focus states, active states — handle with CSS (:hover, :focus-within, .group:hover) before reaching for Stimulus.


Accessibility (ARIA)

Always add ARIA attributes. This is not optional:

  • Interactive elements: aria-label, aria-expanded, aria-controls, aria-haspopup as appropriate
  • Images: meaningful alt text, or alt="" + aria-hidden="true" for decorative images
  • Icons used as buttons: aria-label on the button, aria-hidden="true" on the SVG
  • Form fields: always associated <label>, aria-describedby for hints/errors
  • Dynamic content: aria-live regions where content updates without navigation
  • When refactoring or adding interactivity, review ARIA impact — don't break existing roles

Greek Typography

CSS text-transform: uppercase leaves Greek tonos accents in place (e.g. Ά instead of Α), which is incorrect. The utility at resources/js/utils/strip-accents.js handles this automatically for any element with the uppercase Tailwind class. Always add the uppercase class to elements rather than applying text-transform: uppercase in CSS — this keeps the strip-accents utility working without extra configuration.


Performance & Core Web Vitals

Every frontend change should assume it will be measured by PageSpeed/Lighthouse. These rules exist because we hit each of these mistakes in production and had to fix them after the fact — bake them in up front instead.

CSS bundling

  • Before adding a CSS import to a shared/global entry point (e.g. app.css), ask whether every page actually needs it. Page- or component-specific styles (date pickers, legal-page typography, admin-only widgets) should not ship on pages that don't use them.
  • Scope non-critical CSS to load only where it's used — either a dedicated build entry for that page, or import the .css file inside the same JS module that already lazy-loads that component (e.g. a Stimulus/JS controller), so the bundler emits it as a separate chunk fetched only when that component actually mounts.
  • Exception: never defer CSS for anything the page shows immediately on load (above-the-fold content, or a component that renders as soon as its JS runs — an FAQ accordion, a cookie-consent banner). Even a well-scoped async CSS chunk arrives a beat after the JS that displays the element, causing a visible flash of unstyled/wrong-state content and a layout shift. Keep that CSS in the blocking bundle on purpose.
  • Never import a full icon-font library (Phosphor, Font Awesome, etc.) — it ships every icon regardless of usage. Use inline SVGs for only the icons actually referenced; check the shared icon component for an existing icon before adding a new one.
  • Only declare design tokens / CSS custom properties that are actually used somewhere in the codebase (Tailwind v4 already tree-shakes unused @theme tokens from output, so this is for keeping the source legible, not for bytes).

Fonts

  • font-display: swap means any text using a font weight will visibly reflow once that weight's file finishes downloading, if the fallback font's metrics differ. Preload every font weight used in above-the-fold text, not just the default body-copy weight — bold/black heading weights are usually the most visually prominent element on the page and cause the most visible reflow if not preloaded.
  • When introducing a new heavy-weight text utility (e.g. a bold/black heading class) in a hero or above-the-fold component, confirm that weight has a matching <link rel="preload" as="font"> in the layout <head>.

Images

  • Any image that could be the Largest Contentful Paint element (hero/banner images) must have: fetchpriority="high", loading="eager", explicit width/height, a responsive srcset/sizes, and a <link rel="preload" as="image"> in <head> with matching imagesrcset/imagesizes — this lets the browser start the fetch as soon as it parses <head>, instead of waiting to discover the <img> tag in the body.
  • Every other image: loading="lazy" with explicit width/height to reserve its layout space.

Layout shift (CLS)

  • Never animate top/bottom/left/right/width/height/margin on any element that appears or moves without direct user interaction (banners, toasts, slide-in modals, accordions). These are layout-affecting properties — the browser recalculates layout on every animation frame, and each frame's position delta counts toward CLS. Animate transform (translate/scale) and opacity instead — both are compositor-only and fully excluded from CLS scoring, no matter how far or long the animation runs.
  • This applies to vendor/third-party CSS too (cookie-consent widgets, modal libraries) — check its default show/hide mechanism before shipping, and override it if it animates a layout property directly.
  • Any element with a collapsed/expanded default state (accordions, dropdowns) needs its collapsed-state CSS in the blocking stylesheet, never a lazily-loaded chunk — otherwise it renders in its expanded/default state for a frame before the async CSS applies.

Forms

Never add novalidate to forms. Always use the browser's default validation.


Asking Questions

When the approach is unclear — layout structure, whether something should be a component, which Tailwind pattern fits — ask before building. A short question is cheaper than a refactor.