[DOC] skills: update odoo-javascript skill documentation
This commit is contained in:
parent
158933e96e
commit
162ba0b570
6 changed files with 875 additions and 1 deletions
191
.claude/skills/odoo-web-design/references/responsive.md
Normal file
191
.claude/skills/odoo-web-design/references/responsive.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
# Responsive layout
|
||||
|
||||
## Breakpoints — Bootstrap 5, and only these
|
||||
|
||||
| Name | `min-width` | Typical device |
|
||||
| --- | --- | --- |
|
||||
| (base) | — | phones, 320–575 px |
|
||||
| `sm` | 576px | large phones |
|
||||
| `md` | 768px | tablets |
|
||||
| `lg` | 992px | small laptops |
|
||||
| `xl` | 1200px | desktops |
|
||||
| `xxl` | 1400px | large desktops |
|
||||
|
||||
Mobile-first, `min-width` only:
|
||||
|
||||
```css
|
||||
/* ✅ base = mobile, enhance upwards */
|
||||
.products-grid {
|
||||
display: grid;
|
||||
gap: var(--ac-space-md);
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.products-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.products-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* ❌ desktop-first bands: overlapping ranges, unclear cascade, private breakpoints */
|
||||
@media (max-width: 1599px) and (min-width: 1400px) { … }
|
||||
@media (max-width: 720px) { … }
|
||||
```
|
||||
|
||||
## Prefer intrinsic layout — most grids need no media query at all
|
||||
|
||||
```css
|
||||
.products-grid {
|
||||
display: grid;
|
||||
gap: var(--ac-space-md);
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
|
||||
}
|
||||
```
|
||||
|
||||
`min(100%, 16rem)` is what keeps it from overflowing at 320 px. Reach for a media query only when the
|
||||
layout must genuinely *change* (sidebar moves, table becomes cards), not merely resize.
|
||||
|
||||
## Fluid type and space
|
||||
|
||||
```css
|
||||
:root {
|
||||
--ac-text-sm: clamp(0.875rem, 0.85rem + 0.1vw, 0.9375rem);
|
||||
--ac-text-base: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
|
||||
--ac-text-lg: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
|
||||
--ac-text-xl: clamp(1.5rem, 1.3rem + 1vw, 2.25rem);
|
||||
--ac-space-md: clamp(1rem, 0.9rem + 0.5vw, 1.5rem);
|
||||
}
|
||||
```
|
||||
|
||||
One continuous scale beats three step-changes at breakpoints. Never redefine a token inside a media
|
||||
query to make it smaller.
|
||||
|
||||
Body copy: 1rem minimum, line-height ≥ 1.5, measure 45–75 characters (`max-width: 65ch`).
|
||||
|
||||
## Touch targets and pointer
|
||||
|
||||
WCAG 2.2 AA (§2.5.8 Target Size, Minimum) asks for **24×24 px**. The familiar **44×44 px** is §2.5.5,
|
||||
level **AAA** — treat it as the house rule for anything a thumb hits, applied by pointer type rather
|
||||
than by viewport width: a 1024 px tablet needs the big target, a 1024 px laptop doesn't.
|
||||
|
||||
```css
|
||||
.qty-control .btn,
|
||||
.tag-filter-badge {
|
||||
min-width: 2.25rem; /* 36px — comfortable with a mouse */
|
||||
min-height: 2.25rem;
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
.qty-control .btn,
|
||||
.tag-filter-badge {
|
||||
min-width: 2.75rem; /* 44px */
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.product-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--ac-shadow-md);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hover effects belong inside `@media (hover: hover)` — on touch they stick after a tap and look broken.
|
||||
|
||||
## Tables → cards, without restructuring
|
||||
|
||||
```css
|
||||
@media (max-width: 767.98px) {
|
||||
.checkout-summary-table thead {
|
||||
/* keep it in the a11y tree; hide visually only */
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
}
|
||||
.checkout-summary-table tr {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--ac-space-xs);
|
||||
padding: var(--ac-space-sm) 0;
|
||||
border-bottom: 1px solid var(--ac-border-light);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the one place a `max-width` query is honest: it's an override of the desktop table. Keep the
|
||||
markup semantic — never emit different HTML per viewport.
|
||||
|
||||
## Sticky bars (cart / checkout actions on mobile)
|
||||
|
||||
```css
|
||||
.checkout-actions {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: var(--ac-z-sticky);
|
||||
padding: var(--ac-space-sm);
|
||||
padding-bottom: calc(var(--ac-space-sm) + env(safe-area-inset-bottom, 0px));
|
||||
background: var(--ac-surface, #fff);
|
||||
box-shadow: 0 -2px 8px rgb(0 0 0 / 8%);
|
||||
}
|
||||
```
|
||||
|
||||
`env(safe-area-inset-bottom)` keeps the button above the iOS home indicator. Sticky elements must not
|
||||
eat more than ~20 % of a 568 px-tall viewport.
|
||||
|
||||
## Images
|
||||
|
||||
```xml
|
||||
<img class="product-card-img" t-att-src="product['image_url']" t-att-alt="product['name']"
|
||||
loading="lazy" decoding="async" width="400" height="400"/>
|
||||
```
|
||||
|
||||
- `width`/`height` always — they reserve the box and prevent layout shift (CLS).
|
||||
- `loading="lazy"` for everything below the fold; **never** on the LCP image (first product row,
|
||||
hero) — that delays the largest paint.
|
||||
- CSS: `max-width: 100%; height: auto;` plus `object-fit: cover` with a fixed `aspect-ratio` when
|
||||
cards must align.
|
||||
- Odoo serves resized variants: `/web/image/product.product/<id>/image_256` etc. Ask for the size you
|
||||
render, not `image_1920` scaled down in CSS.
|
||||
|
||||
## Container and page rhythm
|
||||
|
||||
```css
|
||||
.eskaera-page {
|
||||
width: 100%;
|
||||
max-width: 75rem;
|
||||
margin-inline: auto;
|
||||
padding-inline: clamp(1rem, 4vw, 2rem);
|
||||
}
|
||||
```
|
||||
|
||||
Use logical properties (`margin-inline`, `padding-block`, `inset-inline-start`) — Odoo auto-generates
|
||||
an RTL stylesheet, and logical properties survive the flip without a second rule.
|
||||
|
||||
## Test matrix
|
||||
|
||||
| Width | Why |
|
||||
| --- | --- |
|
||||
| 320 px | smallest supported; the one that breaks first |
|
||||
| 360 / 390 px | real Android / iPhone |
|
||||
| 768 px | tablet portrait, first breakpoint |
|
||||
| 1024 px | tablet landscape / small laptop |
|
||||
| 1280 / 1440 px | desktop |
|
||||
| 1280 px @ 200 % zoom | WCAG 1.4.4 reflow — equivalent to 640 px |
|
||||
|
||||
Run the stack (`docker-compose up -d`, http://localhost:8070) and check, per viewport: no horizontal
|
||||
scroll, no clipped text, touch targets still ≥ 44 px on coarse pointers, sticky bars not covering
|
||||
content, images not stretched.
|
||||
|
||||
DevTools device toolbar is enough for layout; check touch targets by actually tapping on a phone once
|
||||
before calling it done.
|
||||
Loading…
Add table
Add a link
Reference in a new issue