// Provides project-specific guidelines for Phoenix development including workflow, Phoenix v1.8 conventions, Tailwind CSS v4, asset bundling, and UI/UX best practices. Use when setting up development workflow, working with assets, styling, or ensuring project conventions.
| name | project-guidelines |
| description | Provides project-specific guidelines for Phoenix development including workflow, Phoenix v1.8 conventions, Tailwind CSS v4, asset bundling, and UI/UX best practices. Use when setting up development workflow, working with assets, styling, or ensuring project conventions. |
This skill provides project-specific guidelines for developing this Phoenix application.
mix precommit alias when done with all changes:req (Req) library for HTTP requests:httpoison, :tesla, and :httpc# Use Req for HTTP requests
Req.get!("https://api.example.com/data")
Req.post!("https://api.example.com/users", json: %{name: "Alice"})
<Layouts.app flash={@flash} ...> which wraps all inner contentMyAppWeb.Layouts module is aliased in the my_app_web.ex file—no need to alias it again<Layouts.app flash={@flash} current_scope={@current_scope}>
<%!-- Your content here --%>
</Layouts.app>
When you encounter errors with no current_scope assign:
current_scope to <Layouts.app>Fix by moving routes to proper live_session and ensure you pass current_scope as needed.
<.flash_group> component to the Layouts module<.flash_group> outside of the layouts.ex modulecore_components.ex imports <.icon name="hero-x-mark" class="w-5 h-5"/> for hero icons<.icon> component for iconsHeroicons modules or similar<.icon name="hero-user" class="w-6 h-6" />
<.icon name="hero-envelope" class="w-5 h-5" />
<.input> component from core_components.ex when available<.input> will save steps and prevent errors<.input field={@form[:email]} label="Email" type="email" />
<.input field={@form[:password]} label="Password" type="password" />
Class Override Warning: If you override default input classes with custom values, no default classes are inherited. Your custom classes must fully style the input.
Use Tailwind CSS classes and custom CSS rules to create polished, responsive, and visually stunning interfaces.
Tailwind CSS v4 no longer needs a tailwind.config.js and uses new import syntax in app.css:
@import "tailwindcss" source(none);
@source "../css";
@source "../js";
@source "../../lib/my_app_web";
Important:
phx.new@apply when writing raw CSS in Tailwind v4Out of the box only the app.js and app.css bundles are supported:
src or link href in the layouts<script>custom js</script> tags within templatesExample (in assets/js/app.js):
// Import vendor libraries
import Chart from "chart.js/auto"
import Alpine from "alpinejs"
// Make available globally if needed
window.Chart = Chart
window.Alpine = Alpine
Alpine.start()
Produce world-class UI designs with focus on:
Implement subtle micro-interactions:
<button class="
px-4 py-2 bg-blue-600 text-white rounded-lg
transition-all duration-200
hover:bg-blue-700 hover:shadow-lg
active:scale-95
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
">
Click me
</button>
Ensure clean typography, spacing, and layout balance for a refined, premium look:
All pages should follow a consistent page header pattern:
<!-- Page Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-8">
<div>
<h1 class="text-2xl md:text-3xl font-bold tracking-tight text-base-content">
Page Title
</h1>
<p class="mt-1 text-sm text-base-content/60">
Description text
</p>
</div>
<div class="mt-4 sm:mt-0 flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
<!-- Action buttons -->
</div>
</div>
Key Classes:
text-2xl md:text-3xl font-bold tracking-tight text-base-contentmt-1 text-sm text-base-content/60mt-4 sm:mt-0 flex flex-col sm:flex-row items-stretch sm:items-center gap-3Responsive Behavior:
Use DaisyUI semantic color classes for automatic dark mode support:
| Purpose | Use | Avoid |
|---|---|---|
| Primary text | text-base-content | text-gray-900, text-black |
| Secondary text | text-base-content/60 | text-gray-600 |
| Muted text | text-base-content/40 | text-gray-400 |
| Primary background | bg-base-100 | bg-white |
| Secondary background | bg-base-200 | bg-gray-100 |
| Borders | border-base-300 | border-gray-200 |
Status Badge Pattern:
defp status_badge_class(:draft), do: "bg-base-200 text-base-content"
defp status_badge_class(:published), do: "bg-success/20 text-success"
defp status_badge_class(:archived), do: "bg-warning/20 text-warning"
Consistent card pattern across all pages:
<div class="bg-base-100 rounded-lg shadow-md border border-base-300 p-6 transition-shadow hover:shadow-lg">
<!-- Card content -->
</div>
Focus on delightful details:
Card with hover effect:
<div class="
group p-6 bg-white rounded-xl shadow-sm
transition-all duration-300
hover:shadow-xl hover:-translate-y-1
">
<h3 class="text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors">
{@title}
</h3>
<p class="mt-2 text-gray-600">{@description}</p>
</div>
Loading state:
<div :if={@loading} class="flex items-center justify-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
mix precommit before committing@apply in raw CSStext-base-content, bg-base-100, etc.)