Pseudo-elements and conditions
A layer's own style is its inline style. Some CSS cannot be said inline: a ::before that draws an arrow head, a + after a menu title that turns into − when the menu opens, a grid that stacks when its container gets narrow, a colour under the pointer. A layer can hold these too, and they render the same on the canvas, in your agent's screenshots, in exports and in code.
What a layer can hold
Each layer has a list of blocks. A block is a pseudo-element, a condition, or both, with its own properties:
| Block | Means |
|---|---|
::before, ::after | A box before or after the layer's content. It shows only with a content property; content: "" draws an empty box you style. |
::marker, ::placeholder, ::selection, ::first-letter, ::first-line | The parts of a layer CSS gives these names. |
@container (max-width: 560px) | Applies while the nearest container around the layer is at most 560px wide. @container card (…) asks the container named card. |
@media (max-width: 760px) | Applies while the artboard is at most 760px wide. See below. |
:hover, :focus, :focus-visible, :focus-within, :active | Applies in that state. |
[open] | Applies while a details layer is open. |
parent:hover, parent[open] | Applies while the parent is in that state. |
Parts combine: @container (max-width: 560px) ::after, @media (max-width: 760px) :hover, parent[open]::after. A query can use a breakpoint token, as in @media (max-width: var(--bp-md)).
In the inspector
The Pseudo-elements and conditions section lists a layer's blocks. The plus adds one: pick a pseudo-element, type a condition, and write the first property, such as content: "+". Each block has its properties as text, edited like Other styles, and a bin to remove it. In the layers panel, ::before and ::after show as rows under their layer, with their content. A click on one selects the layer.
Containers
A container query needs a container: a layer around it with container-type: inline-size (or size) in its style, and container-name if you want to ask for it by name. On the canvas the query answers to that layer's real width, so resizing the container, or the artboard it sits in, shows the change at once.
Media queries read the artboard
A web page's media queries ask about the browser window. On the canvas there is no window per artboard, so Bogen reads a media width query against the artboard the layer is in: an artboard of 390 is a phone, whatever the size of your screen. The same goes for screenshots, video, PDF, responsive_check and layer_styles_computed with a width, so your agent sees what you see. Under the hood the artboard becomes a container named bogen-artboard, and the query a container query on it.
- Only queries about the width are read this way. Height, orientation and aspect ratio queries, and others such as
prefers-reduced-motion, ask the window you see the canvas in. prefers-color-scheme: darkapplies inside a layer, artboard or page in the theme mode nameddark, andlighteverywhere else.- An artboard that hugs its content has no width of its own, so its media queries do not apply. Give it a fixed width.
- The artboard's own media blocks are read against its stored width, as a container query cannot ask about the element it is on. Preview mode at another width does not change those.
- In preview mode, the device widths and the W field change the artboard's width, which is the way to watch media and container conditions take effect.
- An exported HTML page writes the media queries as they are, so there they ask about the window again, which is what they mean on a real website.
States
Hover, focus and active blocks apply in preview mode and in exported pages. While you edit, the canvas leaves them out, so that pointing at layers does not change them. [open] blocks follow the open attribute of a details layer, in editing too.
From HTML
When your agent writes HTML with a <style> block, the rules land on the layers their selectors match, as plain rules do:
<style>
pre[data-lang]::before { content: attr(data-lang); display: block }
.menu > summary::after { content: "+" }
.menu[open] > summary::after { content: "−" }
.row { display: grid; grid-template-columns: repeat(3, 1fr) }
.diagram { container-type: inline-size }
@container (max-width: 560px) { .row { grid-template-columns: 1fr } }
</style>
attr()of an attribute Bogen does not keep on a layer, such asdata-lang, becomes its text.attr()of a kept one, such astitle, stays live.counter()works withcounter-resetandcounter-incrementin the layers' styles.@supportsand@layerare read through.- Not kept, and listed in a warning: a condition inside another condition, a state further up than the parent, a state inside
:not()or:has(),:visited, and pseudo-elements outside the list above. Resets that apply to every element, such as*::before { box-sizing: border-box }or a focus ring on everything, stay with the page.
Snapshot
A captured page keeps working as before: its ::before and ::after become child layers named Before and After, or after their text, at the size the page had. Conditions are not captured, as a capture is the page at one width. If you or your agent add a ::before to a captured layer that already has such a child, it shows twice, and layer_style says so. The page's hover and focus rules stay a record your agent reads with interaction_rules; it can turn one into a :hover block with layer_style.
In exports and code
- HTML export and Copy as HTML put the rules in a
<style>element, written for a class per layer such asbgn-k3j4h5l6m7. Importing that HTML again gives the same blocks. - React with inline styles carries a
<style>element in the component. React with Tailwind uses variants where one says the same:before:content-['+'],hover:,max-[760px]:,@max-[560px]:,dark:. A block with no variant, such as a parent's state, stays in the<style>element. - SVG export keeps a layer with a pseudo-element as HTML inside the SVG, and the native vector export lists it as such.
For agents
layer_style takes pseudo and when, and clear_block removes a block. layer_tree shows which blocks a layer has, layer_get their properties, and layer_styles_computed reads a pseudo-element or lays the artboard out at a width first. design_lint and brand_check look at colours, sizes and fonts inside blocks too. See the MCP tools.
Sync and older versions
Blocks are stored per property, so two people or devices changing different blocks, or different properties of one block, both keep their change. The first block on a layer made before this existed is the exception: if two devices each give that layer its first block while apart, one side's first blocks are kept. A device running an older Bogen ignores blocks: it shows the layer without them and leaves them in place.