Styles
A perspective says which statements to show. A stylesheet says how they are laid out and drawn: what stands on a line, what is nested and indented, where captions go, how wide things are. Every ontology and every topic can carry styles of its own, edited in the app through the built-in Styles view, and your profile can carry styles that apply everywhere you go.
Who this page is for. The built-in styles are designed to cover what a regular RDF analyst or data author needs, and most people never change them. This page is for power users, preferably with some programming background, who want a view to look different from the default. If you know CSS, much of it will feel familiar: a style has a selector saying what it applies to and a set of properties it sets, and several styles can apply to the same thing.
What styles are for, and what they are not
Styling here means what the word has always meant: changing how information looks. A style decides what stands on a line, where captions go, how much room things take, which colours and marks they wear. It does not decide what information there is, and it does not decide what you can do with it. When you adjust styles you are adjusting the look of a view, not building a user interface.
Three things shape what you see, and each has one job:
| Who | Decides |
|---|---|
| The perspective | Which statements are listed and nested, which parts are shown and editable, and what is allowed: whether a list takes new rows, whether rows can be deleted, what a click on a part does. |
| Styles | How all of that is laid out and drawn. |
| The app | How it behaves: clicks and menus, editing, the actions — adding, copying, deleting — paging, opening a record, undo. The same in every view. |
The last row is deliberate. Because behaviour belongs to the app and not to the view, a perspective you have never seen before works the way every other one does. A style can change how the page control looks, but not whether it is there or what it does; the actions it does not meet at all. A style can look different while a row is open, selected or being edited (av:expanded, av:selected and av:edited), the way a CSS rule can for :hover; but nothing in a stylesheet makes anything happen, and nothing in one can take away something the perspective allows.
If what you want is an interface of your own — your own buttons, custom controls, things that happen on a click — styles are the wrong tool, on purpose. A scripting facility for that is planned as a separate part of Arspec. It will be kept apart from perspectives and styles, so that these two stay what they are: plain descriptions that are safe to open and to share.
Where styles come from
Styles reach a view from four places, and when two of them disagree the one ranked higher wins:
| Ranked as | Kept in | Who sees it |
|---|---|---|
| Your stylesheets | Your profile | You, in every file, on every machine your profile follows you to. |
| Topic styles | The topic's .arspec file | Anyone you hand the topic to. The ontology files stay untouched. |
| In-file styles | The ontology file itself | Anyone who opens the file. The look ships with the data. |
| App defaults | The app | Everyone, always, underneath everything else. |
That is the default ranking, and it is a preference rather than a rule: Preferences → Profile → Styling → Style priority lets you rearrange it, so you can decide that a file's own styling beats yours or the other way round. This is one place Arspec deliberately differs from CSS, where the reader gets no say in how the sources rank.
Editing styles in the app
The Styles view is a built-in perspective that shows the styles a file carries, one record per style, and lets you edit them in place. It is off by default, because most files carry no styles. Switch it on from the perspectives screen (the gear at the end of the tab strip) for whichever file you want to style:
- An ontology file: select the file in the sidebar and switch on its Styles tab. Styles you add here are saved in the file.
- A topic: select the topic itself in the sidebar, one step above its files, and switch on Styles there. Styles you add here apply to every file in the topic and travel with the
.arspecfile. - Your profile: Preferences → Profile → Your perspectives and styles names one file that holds your own perspectives and styles. Open it from there as a topic and style it like any other.
In the view, adding a row — Add to list on the list’s menu, Insert on a style’s (above it), or the action bar’s Add — makes a new style with a Label (a name, for you) and an Order (its place in the file; later styles override earlier ones). Right-click a style to move it up or down. Under Selector, add one row per condition the style requires. A new row starts as av:type av:Record: change it to the condition you mean, from the av: namespace, and give it its value. Under Set Attributes, add one row per property the style sets. A new row starts with the av: namespace chosen and the name left for you to pick. Where a style sets av:content, the items it holds are listed under that row in their order: right-click an item to move it up or down or to add one above it, and click an item to see and edit what it is, shown through the built-in Attributes+. Both lists are described below. A style can also be written by hand in any text editor; it is a few lines of Turtle.

What a style can target
A perspective defines a set of hierarchical lists (represented in style selectors under the element av:Source) containing individual matched statements (represented as av:Record). The app builds those two levels from the perspective automatically. Everything else you see inside a record, such as the line its parts stand on, the caption before a value and the block of nested lists under a row, is put there by a style. The built-in stylesheet puts it there by default, and any style of yours can change or replace it.
A style applies to entries, and every entry is exactly one element. Below is the list of all the potential targets whose presentation can be altered by a style:
| Element | What it is |
|---|---|
av:Source | One list of a perspective, top-level or nested. The top-level list is the one av:root true finds. |
av:Record | One row of a list, that is, one matched statement. Everything inside a record repeats for every row. |
av:Term | One displayed part of a row: its subject, its relation or its value. |
av:Layout | A container a style has added: a line of fields, a form, a grid. |
av:Main | The record's row: what it shows of its own statement. Every record has one. |
av:Details | What hangs under the row: the nested lists, or the frame of lenses when the record is expanded. Every record has one, and how far it is indented is its own av:paddingStart. |
av:Text | A piece of fixed text, or the list's own label placed as a cell. |
The elements are a fixed set, and an entry’s element is fixed too: it is decided once, when the entry is created — by the app for lists, records and the page control, by the content item for everything else — and nothing changes it afterwards. No style can add an element to an entry or take one away, and it is the same in every state. It is the one thing about an entry you can always rely on. The open-ended way to mark things for styling is extra types: further classes on a content item, or on a list, described further down.
The shape of a style
A stylesheet is a Turtle file, or a part of one. Each style is an av:Style node with a label, an order, a selector and the properties it sets. Here is one from the built-in sheet:
@prefix av: <https://arspec.org/ars/spec/1/schema/view#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
[ a av:Style ] rdfs:label "a predicate in a line keeps to its cap" ;
av:order 2 ;
av:select [ a av:Selector ;
av:element av:Term ; av:role rdf:predicate ; av:parentType av:Line ] ;
av:set [ av:maxWidth "150px" ] .
| Part | Says |
|---|---|
rdfs:label | The style's name, for people reading the sheet. |
av:order | Its position in the sheet. Later styles override earlier ones, so this is also its rank among the styles in the same sheet. It is a decimal number: the app writes 0, 1, 2 and so on, and you can insert a style at 1.5 without renumbering. |
av:select | The selector: the conditions an entry must meet for the style to apply. Leave it out and the style applies to everything. |
av:set | What the style sets: any of the properties, one per line, and — for a list, a row, a record or its details — its content, all on this one node. |
Selectors
A selector is a list of conditions. Every condition stated must hold; a condition not stated is not checked. There is no or: if a style should apply in two different situations, write it twice. A condition from this vocabulary that the app does not know — a misspelt one, one from a newer version, one since retired such as av:maxDepth — switches the whole style off, because ignoring it would make the style apply in more places than you wrote it for.
| Condition | Holds when |
|---|---|
| What the entry is | |
av:element | The entry is that element (see the table above). |
av:role | The entry is a term showing that part of the statement: rdf:subject, rdf:predicate or rdf:object. |
av:type | The entry is typed with that class: its element, or one of the extra types its item was given. Extra types are class names you choose yourself; see below. |
| Where it stands | |
av:parentElement, av:parentType | The entry's immediate container is that element, or is typed with that class. Only one step up. |
av:root | true: the entry belongs to the perspective’s top-level list — the list itself, its rows, and everything in them that is not a nested list. false: it belongs to a nested list. It is about which list, never how deep: drawing a box round the rows, or round a row, changes neither answer. A perspective shown through an invocation is a view of its own, and its top-level list is the root of that one. |
| Which list it belongs to | |
av:sourceId | The nearest list is the one with that ID. The way to style one particular list by name. |
av:sourceType | The nearest list declares that class among its Extra types. Works like a CSS class; see below. |
av:showsCount | The nearest list displays exactly that many parts of each statement. Distinguishes a single field from a line of several. |
av:maxCount, av:minCount | The nearest list's ceiling, or its floor, is exactly that number: av:maxCount 1 is a list meant to hold one record. A list with no ceiling matches no av:maxCount; a list that states no floor has one of 0. |
av:hasLabel | Whether the nearest list has a label: true or false. What lets a style dress the labelled lists, or the unlabelled ones, without naming each. |
| What the statement says | |
av:recordSubject, av:recordPredicate, av:recordObject | The statement the entry is drawn for — the nearest record's — has that subject, predicate or object: an IRI, or a literal by its text. True of the record and of everything inside it, nested lists included, until a nested record begins. |
| What is happening right now | |
av:edited | The term's editor is open. True of that one field only. |
av:selected | The entry is the selected record or list, or one of its own parts: a record’s row and the fields in it, a list’s heading. It stops at whatever is nested: the rows of a selected list are not selected. False for everything while the view as a whole is selected. |
av:expanded | The entry is inside a record the user has expanded, or on a record's own page. True of the record and of everything inside it; add av:element to mean just the record. |
The conditions in the first three groups describe the structure of the view, which is the same for every row. The last two groups describe one row: what its statement says, and what it is doing at the moment — and the latter is how a view can change shape while it is being used, for instance by giving a field more room only while you are typing in it.
Which style wins
Several styles will usually apply to one entry. Each property is resolved separately, and the highest-ranked style that sets it wins. Unlike CSS, there is no specificity: a selector only decides whether a style applies, and a longer selector carries no extra weight. The ranking is:
- Where the sheet comes from: your stylesheets, the topic's, the file's own, the app's defaults, in the order set under Preferences → Profile → Styling → Style priority.
- Own before imported. Within one of those, a file's own styles beat the styles of files it imports.
- Position in the sheet. Within one sheet, the style with the higher
av:orderwins, so a sheet reads top to bottom with refinements after the basics. - What the entry itself states, if anything, wins over every style.
Two consequences worth knowing. First, because resolution is per property, a style that sets av:rowGap leaves av:paddingStart to whoever set it, so you can override one thing without restating the rest. Second, a property that no style sets takes the default given in the vocabulary, and defaults are never written to a file.
Properties a style can set
Every property is independent and optional. Lengths and track lists are written as strings; named choices are terms from the av: namespace.
Lengths
One way of writing a length, used for widths, heights, gaps, padding and grid tracks alike:
| Written | Means |
|---|---|
auto | As big as its content. |
120px | A fixed size in logical pixels, which the theme scales. |
50% | A fraction of the container's inner size on that axis. |
1fr | A share of whatever room is left once everything else has taken its size, as in CSS grid. |
80px..30% | A range: any of the above, clamped between a minimum and a maximum. |
related | A spacing token the theme resolves to a size: none, tight, related, unrelated, section, indent. |
Prefer the tokens for gaps and padding. They say what the spacing means rather than how many pixels it is, so a sheet reads right on any screen and a density setting rescales a whole view at once. Sides are named start and end rather than left and right, so right-to-left languages work without changes.
An entry's own size and placement
| Property | Says | Default |
|---|---|---|
av:width, av:height | Its size, as a length. | auto |
av:minWidth, av:maxWidthav:minHeight, av:maxHeight | Limits on its size. A share (1fr) with a maximum stops growing at the maximum, and the room it does not take goes to the other shares. | none |
av:paddingStart, av:paddingTop, av:paddingEnd, av:paddingBottom | Room inside its own edge. A nested list is indented by its padding. | none |
av:justifySelf, av:alignSelf | How it sits in the room it is given, horizontally and vertically: av:start, av:center, av:end, av:stretch, av:baseline. | the container's setting |
av:widthGroup, av:heightGroup | A size group it belongs to, for aligning things into columns. | — |
av:gridColumn, av:gridRow | Where it stands in its container's grid: "2", "1..3", "span 2". | placed automatically |
av:gridArea | A named area from the container's av:areas. Takes precedence over the two above. | — |
av:drawOrder | Its order among its siblings when drawn. (Not av:order, which orders styles and items as written.) | as written |
av:x, av:y | Its position inside a canvas container. Ignored elsewhere. | — |
There are no margins. An entry never pushes its neighbours; all spacing belongs to the container, through gaps and its own padding.
A container's layout of its children
Every container is a grid, and a row or a stack is simply a grid with one track.
| Property | Says | Default |
|---|---|---|
av:direction | The axis children are laid along: av:column or av:row. | av:column, a stack |
av:columns, av:rows | Track sizes, separated by spaces: "auto 1fr", or "auto-fill(120px)" for as many as fit. | one auto column |
av:rowGap, av:columnGap | Space between children on each axis. | related |
av:justify, av:align | How children sit in their cells when they do not say themselves. | stretch / start |
av:areas | Named cells, one name per cell with rows separated by " / ": "header header / nav main". A name repeated across neighbouring cells spans them. | — |
av:layout | The kind of container: av:grid, av:canvas, or an IRI of your own. An unknown kind is drawn as a grid. | av:grid |
av:reverse | Whether children are laid from the end. | false |
The one grid covers most layouts you would want: av:direction av:row is a line, av:columns "auto 1fr" is a label-and-value form, av:columns "auto-fill(12em)" wraps items, and two children sharing one cell overlay each other.
Text and decoration
| Property | Says | Default |
|---|---|---|
av:labelPlacement | Where the entry's label is drawn: av:heading above its content, av:leading before it on the same line, or av:none. | av:heading |
av:labelRole | The text role the label is drawn in: av:heading, av:caption, av:body. | av:heading |
av:textRole | The text role the entry's own text is drawn in: av:body, av:heading, av:caption, or av:code for text the theme should set in its code face. | av:body |
av:color | The colour the entry's text is drawn in — its caption along with its value. | the role's own |
av:background | A colour behind the entry, filling the room its geometry gave it. | no box at all |
av:bold, av:italic | Whether the text is emphasised: true or false. Stated false they take the emphasis off text whose role carries one. | the role's own |
av:decoration | The line drawn on the text: av:underline, av:lineThrough or av:none. | the role's own |
av:connector | Whether a line is drawn from a record's main term to the things nested under it: av:line or av:none. | av:none |
av:frameColor | The colour of the frame drawn round the selected record or list: a theme role, or a literal — as every colour is, below. Set it on records, on lists, or on both. | av:primary |
av:frameWidth, av:frameCorner | The frame's thickness and corner radius, in logical pixels. | 1, 1 |
av:frameFit | How the frame wraps the things nested under the row: av:hug follows them, av:record spans the record's full width. | av:hug |
A sheet names roles, never fonts: av:caption says what a piece of text is, and the theme decides what a caption looks like, in light and dark alike. There is deliberately no font family and no font size — a size would also be measured into the av:widthGroup caps that keep columns in line, so a style that set one would quietly break them. If you want bigger text, name a role that is bigger.
Colours are named the same way. A colour is a theme role — an accent (av:primary, av:secondary, av:tertiary, av:error), an edge (av:outline, av:outlineVariant), a surface something may stand on (av:surface, av:primaryContainer and its fellows), or the colour text takes standing on one of those (av:onSurface, av:onSurfaceVariant, av:onPrimaryContainer, …) — or a literal "#RRGGBB" / "#AARRGGBB" where nothing in the theme is the colour meant. Prefer the role: a sheet written in hex is a sheet that is wrong in half the themes.
A mark is the entry's own, and nothing inherits it. Colouring a line does not colour the terms standing in it; say what you mean about those, and av:parentType is how a style names them. The marks on a term reach its caption and its stored text — not the type tag beside it, and not what a search picked out of it: those belong to the cell, not to the style.
A property that is not in the vocabulary is not an error; it is kept and written back untouched, in case a future version or another tool understands it.
Content: what stands in a list and in a record
The same styles that say how things look also say what stands where. That is still presentation: a style arranges the information the perspective provides, and whatever text you add round it, and nothing it places does anything. Every list and every record is a small tree the app builds for you and a style fills in: a list (av:Source) holds its records, and a record holds a Main — its row — and a Details — what hangs under the row, where its nested lists live. These four always exist. What each contains is stated with one property, av:content:
| On | av:content states |
|---|---|
a list (av:Source) | Where its records go, and anything shown once per list round them: a line of text over the rows, a note under them, a box round the rows alone. |
a row (av:Main) | Which parts of the statement stand on it, and how they are arranged. Most of what a record looks like is decided here. |
a record’s details (av:Details) | What hangs under the row, beside the nested lists that are always there. |
a record (av:Record) | Where its Main and its Details stand, and anything beside them. Rarely needed — unless you do without the two. |
The Main and the Details are folders of convenience. They are where the app's own styles put things, and they bring what makes the conventional layout cheap to state: the Main is the row a record is captioned by and framed round, and the Details holds every nested list without a style having to name one, indented as details are. You are not bound to them. A nested list can be placed anywhere in the record — by id, or all of them at once — and a style may put everything on the record itself and leave the two empty; an empty folder stands nothing. The built-in styles assume the convention, so going without it means stating more yourself, and we do not recommend it; but nothing stops you.
The content is a list of av:items, ordered by av:order. An item is typed by what it makes — the same word a selector asks for with av:element:
| Item | Makes |
|---|---|
av:Term | One part of the statement; av:role says which. In a record only: a list has no row of its own, so in a list’s content a term makes nothing. |
av:Layout | A container around a list of its own: its items stand under an av:content, the same way a style's do, so a list reads the same at every depth. Any further class after the a (a av:Layout, av:Line) types it for selectors — and names it, unless av:id says otherwise. |
av:Text | Fixed text — or, with av:labelled, the list’s own label as a cell that can be sized and placed like a field. |
Four more items make nothing: they place something the tree already has. A style cannot remove what they place — leave a marker out, and what it would have placed stands at the end anyway.
| Marker | Places |
|---|---|
av:Records | The list’s records, in the list: among whatever else the list shows once, or inside an av:Layout of the list’s own — a box round the rows, with the rest of the list outside it. |
av:Main, av:Details | The record’s row and its details, in the record. |
av:Sources | Every nested list not yet placed — in the details by convention, but anywhere in the record. |
av:Source + av:sourceId | One nested list, or one invocation, by the id it was declared with — anywhere in the record. Placed, it is out of the details. Markers take their turn in content order, the record's before the row's before the details', so one named after av:Sources finds nothing left. |
Two rules make this predictable:
- Content is replaced whole, never merged. Of the styles that state content for one of the four, the winner supplies the entire list — and each of the four is resolved on its own, so restyling a row does not disturb what hangs under it.
- An item with nothing to show is left out. A term for a part the list does not display, a button the list does not allow, a layout whose every item came to nothing: none of these is an error, and none leaves an empty box. This is why one built-in style can describe a line of three fields and still look right for a list that shows only the value — and why a record with nothing under it draws no details at all.
Coordinating across a view
A style cannot read what another style decided. Everything that has to be coordinated between two places goes through one of a few named channels.
av:type: a class you choose
A content item is typed with its element — a av:Layout — and it can be typed with more: a av:Layout, av:Line. Every class after the element is an extra type, and the entry the item produces keeps them all. A selector matches one with av:type, or matches a container’s with av:parentType. This is the equivalent of a CSS class, written as the RDF class it is: an IRI, so two stylesheets cannot collide on a name, and you can declare and describe your own in your own namespace. The built-in style that builds a record’s line types it av:Line, and the styles that size the fields inside a line match that type without needing to know which style built the line or how deep it sits:
# one style builds the line and types it …
[ a av:Style ] rdfs:label "a record is a line of its shown parts" ;
av:select [ a av:Selector ; av:element av:Main ] ;
av:set [ av:content [
av:item [ a av:Layout, av:Line ; av:order 0 ;
av:content [
av:item [ a av:Term ; av:order 0 ; av:role rdf:subject ] ;
av:item [ a av:Term ; av:order 1 ; av:role rdf:predicate ] ;
av:item [ a av:Term ; av:order 2 ; av:role rdf:object ] ] ] ] ] .
# … and two others find it by the type.
[ a av:Style ] rdfs:label "a line is a row" ;
av:select [ a av:Selector ; av:type av:Line ] ;
av:set [ av:direction av:row ] .
[ a av:Style ] rdfs:label "the fields of a line share it" ;
av:select [ a av:Selector ; av:element av:Term ; av:parentType av:Line ] ;
av:set [ av:width "1fr" ] .
Like the element, an entry’s types are fixed when the entry is created and never change: no style adds a type to an existing entry, and nothing about the current state alters one. An item typed with two elements at once (a av:Term, av:Text) is read as the plainer of the two — the order is Text, Layout, Term, Records, Sources, Main, Details, Source — and the other is dropped.
A class also names what it types. Every entry has an id, and ids are what the app keeps your open editors and expanded records by while a view is rebuilt; a container or a text cell takes its name from its own class, so the line above stands as line without being told to. Two items landing on one name are numbered apart, and nothing a style can write makes two entries with the same id. Write av:id when you would rather pin the name to something of your own choosing than to the class.
av:widthGroup and av:heightGroup: one size, shared
Every entry that names the same size group is made as wide (or as tall) as the widest of them, anywhere in the view, whether or not they are siblings or even come from the same list. This is how a tree of nested lists can line up like a table:
[ a av:Style ] rdfs:label "predicates line up across the whole view" ;
av:select [ a av:Selector ; av:element av:Term ; av:role rdf:predicate ] ;
av:set [ av:widthGroup "predicate" ; av:justifySelf av:start ] .
A group name is global to the view, so name groups for what they mean ("predicate", "label") rather than for where they happen to be used.
av:sourceType: a class declared in the perspective
A list in a perspective can declare extra types under Configuration → Extra types. A style matches one with av:sourceType, and the match holds for that list, its records and everything inside them, stopping where a nested list begins. The perspective says which look it wants; the stylesheet says what that look is. This is the same division of labour as class names in a CSS framework.
The app ships one such type, av:Form: the list draws no heading, and each record becomes one line led by the list's name where the relation would have been. A nested list pinned to one relation then reads as Label value and lines up with its siblings. It takes effect on a list with a label and a ceiling of one, the lists it is for, and changes nothing elsewhere. You can key a style on any IRI you like, and a sheet ranked above the app's may redefine what av:Form means.
av:sourceId: one list, by name
The direct route: address exactly one list of one perspective. Useful, but worth reaching for last, because a style keyed on an ID says nothing about why it applies and stops working the moment the perspective is copied. A type usually says what you meant.
av:edited, av:expanded and av:selected: the current moment
These three conditions describe what one row is doing right now rather than how the view is built, and they are how a view changes shape as it is used. The built-in sheet uses the first, in a pair of styles that is also the clearest example of the ranking at work:
[ a av:Style ] rdfs:label "a predicate in a line keeps to its cap" ;
av:order 2 ;
av:select [ a av:Selector ;
av:element av:Term ; av:role rdf:predicate ; av:parentType av:Line ] ;
av:set [ av:maxWidth "150px" ] .
[ a av:Style ] rdfs:label "a predicate being typed into gets room" ;
av:order 3 ;
av:select [ a av:Selector ;
av:element av:Term ; av:role rdf:predicate ; av:edited true ] ;
av:set [ av:maxWidth "320px" ] .
The width a relation normally gets is enough for reading a name but not for typing one, since an open editor holds two fields and a button. Both styles set the same property, so the ranking decides between them: while the editor is open the second one is later in the sheet and wins, and the field may grow to 320px. The moment the editor closes, the second style stops matching and the 150px cap is back.
false is a condition too: av:edited false means “while this is not being edited”. av:expanded is true of an expanded record and of everything inside it; add av:element av:Record when you mean the record itself. av:selected is narrower on purpose: it is true of the one record or list the cursor is on and of its own parts, and not of what is nested in it, so av:element av:Record ; av:selected true is exactly the record with the frame round it, and av:element av:Term ; av:selected true the fields of that one row.
av:recordSubject, av:recordPredicate, av:recordObject: what the statement says
Three conditions read the data itself: the subject, the predicate and the object of the statement a record is drawn for. A value is an IRI, written as one (rdfs:label, ex:alice), or a literal, written in quotes and matched by its text alone — "42" is the typed 42 and the plain one alike. The condition holds of the record and of everything inside it, a nested list included, and stops where a nested record begins, which has a statement of its own. Like av:edited, this is a condition on one row rather than on the view's structure: a style stating it applies to alice's record and not to bob's.
[ a av:Style ] rdfs:label "a label reads quieter" ;
av:select [ a av:Selector ;
av:element av:Term ; av:role rdf:object ; av:recordPredicate rdfs:label ] ;
av:set [ av:color av:onSurfaceVariant ; av:italic true ] .
A blank node has no name a style could write, and matches nothing. And there is no pattern and no comparison beyond equality: for “every statement a query would select”, give those statements a list of their own — a nested list with a filter — and key the style on its av:sourceId or av:sourceType.
Recipes
Tighten a nested list
[ a av:Style ] rdfs:label "attributes sit close together" ;
av:order 0 ;
av:select [ a av:Selector ; av:element av:Source ; av:sourceId "props" ] ;
av:set [ av:rowGap "tight" ; av:labelPlacement av:none ] .
A label-and-value form instead of a line
[ a av:Style ] rdfs:label "two columns, label then value" ;
av:order 1 ;
av:select [ a av:Selector ; av:type av:Line ] ;
av:set [ av:direction av:column ; av:columns "auto 1fr" ; av:align av:baseline ] .
A quieter frame on the selected record
[ a av:Style ] rdfs:label "the selected record is outlined, not accented" ;
av:order 2 ;
av:select [ a av:Selector ; av:element av:Record ] ;
av:set [ av:frameColor av:outlineVariant ; av:frameCorner "6" ;
av:frameFit av:record ] .
Mark a kind of statement
[ a av:Style ] rdfs:label "inferred statements read quieter" ;
av:order 3 ;
av:select [ a av:Selector ; av:element av:Term ; av:sourceId "inferred" ] ;
av:set [ av:color av:onSurfaceVariant ; av:italic true ] .
Where what you mean is a part of the statement — every rdfs:comment, everything about one subject — av:recordPredicate and its two siblings say it directly. Where it is more than a part can say — every statement a query would select — the list is the data-dependent half: a child datasource filtered to the statements you mean gives them an av:sourceId, or a type on what its rows put on screen gives them an av:type, and either is a name a style can catch. Because marks do not inherit, this one names the terms themselves rather than the row around them.
Draw a line to the nested lists
[ a av:Style ] rdfs:label "nested sections are joined to their master" ;
av:order 4 ;
av:select [ a av:Selector ; av:element av:Details ] ;
av:set [ av:connector av:line ] .
A heading line over a list, and its rows in a box
[ a av:Style ] rdfs:label "what we know, boxed" ;
av:order 5 ;
av:select [ a av:Selector ; av:element av:Source ; av:sourceId "props" ] ;
av:set [ av:content [
av:item [ a av:Text ; av:order 0 ; av:text "What we know" ] ;
av:item [ a av:Layout, ex:Rows ; av:order 1 ;
av:content [ av:item [ a av:Records ; av:order 0 ] ] ] ;
av:item [ a av:Text ; av:order 2 ; av:text "That is all." ] ] ] .
[ a av:Style ] rdfs:label "the box is a quiet panel" ;
av:order 6 ;
av:select [ a av:Selector ; av:type ex:Rows ] ;
av:set [ av:background av:surface ;
av:paddingStart "tight" ; av:paddingEnd "tight" ] .
The list’s content is drawn once, however many rows there are: the first text over them, the last under them, and the rows alone inside the layout, which a second style finds by the class it was given. The page control stays at the foot of the list, outside the box. An empty list draws no box at all, only its texts. The rows are the same rows inside the box: the styles that find them by what they are (av:element, av:type, av:root) find them still. And it is a list of the perspective’s own that takes content: an invocation shows another perspective’s list, which is dressed by the styles written for that one.
Do without the row and the details
[ a av:Style ] rdfs:label "everything on the record itself" ;
av:order 5 ;
av:select [ a av:Selector ; av:element av:Record ] ;
av:set [ av:content [
av:item [ a av:Term ; av:order 0 ; av:role rdf:subject ] ;
av:item [ a av:Source ; av:order 1 ; av:sourceId "about" ] ;
av:item [ a av:Sources ; av:order 2 ] ] ] .
[ a av:Style ] rdfs:label "and nothing on the row" ;
av:order 6 ;
av:select [ a av:Selector ; av:element av:Main ] ;
av:set [ av:content [ ] ] .
The first style puts the subject, then the about invocation, then whatever other lists there are, straight on the record. The second empties the row — without it the built-in line would stand as well, and the subject would show twice. The details, with nothing left to hold, stand nothing. This is not the recommended shape: the built-in styles that indent the details and caption the row assume the two folders, and a sheet that leaves them will be stating those things itself. But nothing stops it.
Read the built-in sheet. Everything you see in Arspec by default comes from one stylesheet of about twenty styles, and it is the shortest complete example of how a view is put together. Preferences → Profile → Your perspectives and styles opens the shipped vocabularies; the sheet itself is assets/view/1/default.ttl in the app's source.