# Responsive grid > Keyboard navigation for a responsive CSS grid, with the column count read from the layout on every keypress so breakpoints stay in CSS. Set `data-keyrove-cols="auto"` on the grid container to read its column count from CSS on every keypress. Navigation then follows layout changes without copying breakpoints into JavaScript. This demo uses a [container query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries). Drag the corner of the dashed frame or narrow the window, then try the arrows. With six columns, ↓ moves from January to July; with two, it moves to March. ```html
``` ```ts import { keyRove } from '@mixedrays/keyrove'; //
document .querySelector('#months') .addEventListener('keydown', (e) => keyRove(e)); ``` Or, where the markup is not yours, as an option: ```ts keyRove(e, { items: '.month', cols: 'auto' }); ``` The demo's grid carries `data-keyrove-root` only because the site's listener sits on the panel around it. With the listener on the grid itself, as above, the attribute is not needed. ## Counted on every keypress `auto` reads the root's computed `grid-template-columns` during each call. No resize listener or observer is needed to keep the count current. ## The stylesheet owns the breakpoints The panel declares itself a container with `container-type: inline-size`; the grid inside sets `--cols` and lays itself out with `grid-template-columns: repeat(var(--cols), minmax(0, 1fr))`; each `@container` rule changes the property and nothing else. Add a breakpoint, or move one, and navigation follows without a JavaScript edit. The query measures the panel's width, so the grid also adapts when placed in a sidebar. The panel is the query container because an element cannot query its own size. ## When the count is implicit A grid built on `repeat(auto-fill, minmax(8rem, 1fr))` never states a count; the browser decides how many tracks fit. `auto` needs nothing more for it: the computed value lists the tracks the browser made, so they are counted the same way. ## What auto counts - The root must be the grid container, with one item per cell in DOM order. Each item must span one track. Spanning items, subgrids and row wrappers do not match this navigation model, including when `items` selects descendants. - Named lines, such as `[full-start]`, are not counted as columns. - If the computed value does not resolve to a list of pixel track sizes, the count falls back to one and the group navigates as a list. This includes a flex container or a grid that is not laid out. ## Layouts that are not grids A `flex-wrap` row has no track list to count, so `auto` sees one column. Copy the count in yourself instead, right before the call. Here the stylesheet publishes it as `--cols`: ```ts months.addEventListener('keydown', (e) => { months.setAttribute( 'data-keyrove-cols', getComputedStyle(months).getPropertyValue('--cols'), ); keyRove(e); }); ``` keyrove reads the attribute fresh on every keypress, so refreshing it in the same handler is all the synchronization this needs. If something else needs the attribute between keypresses, such as a test asserting on it, a `ResizeObserver` on the container can make the same copy whenever the size changes.