diff --git a/pattern-library/source/sass/06-examples/00-page.scss b/pattern-library/source/sass/06-examples/00-page.scss index dcd7046b8..148d2b755 100644 --- a/pattern-library/source/sass/06-examples/00-page.scss +++ b/pattern-library/source/sass/06-examples/00-page.scss @@ -9,6 +9,85 @@ padding: size(2) size(1); } +/** + * A wider shell for an example that has to show two things beside each + * other, such as what a reader typed and what came back. + * + * 800px is a comfortable measure for reading a column of prose, which is + * what most examples are. It is far too narrow once a page puts two + * columns or a table of controls inside it, because the columns then + * halve it and every control in them collapses and truncates. + * + * Everything inside takes the full width. Prose that would otherwise run + * to an unreadable measure goes in c-eg-intro or c-eg-columns below, + * which divide the width into readable columns rather than leaving half + * the page empty. + */ +.c-eg-page--wide { + max-width: 1400px; +} + +/** + * The notes an example opens with, laid across the width rather than + * stacked down the left of it. + * + * A wide page fits three columns of prose beside each other, so what an + * example has to say before it starts takes one band and the example + * itself is on screen without scrolling. Each column keeps a readable + * measure because the columns divide the width, rather than one + * paragraph stretching across all of it. + */ +.c-eg-intro { + display: grid; + grid-template-columns: 1fr; + gap: 0 size(2); + margin-bottom: size(2); + + @include respond-to(md) { + grid-template-columns: 1fr 1fr; + } + + @include respond-to(xl) { + grid-template-columns: repeat(3, 1fr); + } + + > p { + margin-top: 0; + } +} + +/** + * The head of an example, being the logo beside the title and the line + * that says what the example is for. + * + * The logo sits beside the words rather than above them, because a logo + * on its own line costs every reader the height of it before anything + * they came for. + */ +.c-eg-masthead { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: size(0) size(3); + margin-bottom: size(2); + + > img { + flex: 0 0 auto; + } + + &__words { + flex: 1 1 20rem; + + > :first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } + } +} + .c-eg-page__title { @include make-text-heading-2(); } diff --git a/pattern-library/source/sass/06-examples/08-fields.scss b/pattern-library/source/sass/06-examples/08-fields.scss new file mode 100644 index 000000000..e180cbd8f --- /dev/null +++ b/pattern-library/source/sass/06-examples/08-fields.scss @@ -0,0 +1,163 @@ +/** + * Fields for an example that takes input, rather than one that only + * shows what an engine returned. + * + * Almost every example we ship reports data and asks for nothing, so the + * system grew a rich set of ways to display a result and nothing for a + * reader to type into. An example that does take input was left composing + * .b-input with whatever it could find, which reads as a different + * control on the same page. These are the two fields such an example + * needs, being a single line control and a multi line editor, sharing one + * treatment so they look like a set. + */ + +/** + * What both fields share on top of the design system input. The border + * softens and the corners round, so a field reads as an area to work in + * rather than as a bordered rectangle, and a focus ring says which field + * holds the caret. + * + * The ring is on :focus-visible, so a keyboard user gets it and a + * pointer user clicking straight into the field does not. + */ +@mixin make-example-field { + @include make-input(); + border-color: $colour-grey-light; + border-radius: $rounded; + background-color: $colour-grey-lighter; + + &:focus-visible { + outline: 2px solid $colour-blue; + outline-offset: 1px; + border-color: $colour-blue; + } +} + +/** + * A single line control, being a text box or a list. Filling its + * container is not enough on its own, because a control in a narrow + * table column would collapse to the column and truncate what it holds, + * so it asks for a width the column has to give it. + */ +.c-eg-control { + @include make-example-field(); + width: 100%; + min-width: size(11); +} + +/** + * A table of fields, being one row for each thing a reader sets. + * + * Everything inside it is one size. The system sizes each piece for the + * place it usually appears, so a control carries the body size it needs + * standing alone in a form, and the code face is a step smaller because + * it usually sits inside running prose. Put the two in one row with + * ordinary cell text and a reader sees three sizes and reads three + * different things rather than one row. + * + * The control also loses its form padding, because a row of them is + * dense by nature and padding sized for a lone box makes the table tall + * enough to scroll for no reason. + */ +.c-eg-fields { + .c-eg-control, + .b-text--code { + font-size: inherit; + } + + .c-eg-control { + padding: size(-3) size(-2); + } + + /* A table aligns to the top, which is right where a cell holds a + paragraph and the row is as tall as the longest one. Here the tall + cells are controls, which centre what they hold, so a name aligned + to the top sits above the control beside it and nothing in the row + lines up. */ + .c-eg-table__cell { + vertical-align: middle; + } +} + +/** + * Output printed exactly as something produced it, such as a message a + * caller would meet or a canonical form. + * + * It scrolls sideways rather than stretching what is around it. A long + * line inside a bare pre widens its container, and in a page laid out in + * columns that pushes everything else out of shape, which is worse than + * a scrollbar. + */ +.c-eg-pre { + @include make-text-code(); + background-color: $colour-grey-lighter; + border-radius: $rounded; + padding: size(0); + margin: 0 0 size(2); + max-width: 100%; + overflow-x: auto; +} + +/** + * The same block for output that is prose rather than structure, such as + * a message. Prose is easier to read wrapped, whilst something with + * meaningful indentation is not, which is why this is a choice rather + * than the default. + */ +.c-eg-pre--wrap { + white-space: pre-wrap; + word-break: break-word; +} + +/** + * A row of controls that belong together, such as a label, a list to + * choose from and the button that acts on it. + * + * c-eg-button-row sets margins and nothing else, which is all a row of + * buttons needs, and it leaves a control filling the line on its own so + * the button beside it falls underneath. This lays the row out. + */ +.c-eg-controls { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: size(0); + margin: size(1) 0 size(2); + + /* A control in a row sizes to what it holds rather than filling the + line, which is the opposite of what it does in a table cell or a + column, so the row stays a row. */ + .c-eg-control { + width: auto; + } +} + +/** + * A multi line editor for something structured, such as a script, a + * configuration block or a request body. It adds to the single line + * control only what many lines need. + */ +.c-eg-editor { + @include make-example-field(); + @include make-text-code(); + + width: 100%; + min-height: 340px; + + /* make-input sets 1.2, which is right for one line of prose and too + tight for many lines of indented code. */ + line-height: 1.5; + + /* Only the height can be dragged. A reader widening one editor in a + two column layout would push the other out of the grid. */ + resize: vertical; + + /* The formats these examples show are indented two spaces, so a tab + that jumps eight columns makes pasted content look wrong. */ + tab-size: 2; + + /* Lines scroll sideways rather than wrap, because wrapping a + structured format hides the indentation that carries its meaning. */ + white-space: pre; + overflow: auto; +} diff --git a/pattern-library/source/sass/06-examples/09-result.scss b/pattern-library/source/sass/06-examples/09-result.scss new file mode 100644 index 000000000..3475355b5 --- /dev/null +++ b/pattern-library/source/sass/06-examples/09-result.scss @@ -0,0 +1,65 @@ +/** + * The answer an example exists to give, at the weight that deserves. + * + * An example computes one thing, and everything else on its page is + * there to explain that one thing. So the answer is what a reader looks + * for first and what they come back to after every change they make. + * Reported at the size of ordinary body text it reads as one more + * paragraph among the paragraphs explaining it, and a reader has to hunt + * for the part they came for. + * + * Put this above whatever a reader changes rather than below it, so that + * changing something and seeing what it did takes no scrolling. + */ + +.c-eg-result { + background-color: $colour-grey-lighter; + border-radius: $rounded; + padding: size(2) size(3); + margin: size(1) 0 size(3); +} + +/* What the answer is the answer to, said quietly above it, because the + reader already knows and only needs reminding. */ +.c-eg-result__label { + @include make-text-heading-caps(); + display: block; + margin: 0 0 size(-2); +} + +/* The answer itself. It carries the code face because what an example + returns is a value rather than prose, and a size that makes it the + first thing on the page a reader sees. */ +.c-eg-result__value { + @include make-text-code(); + font-size: size(7); + line-height: 1.1; + color: $colour-black; + margin: 0; + + /* A value can be a long string, and stretching the panel to fit one + would push the page out of shape. */ + word-break: break-word; +} + +/* How the answer should be read, said quietly below it. */ +.c-eg-result__note { + margin: size(0) 0 0; +} + +/** + * Where the example has no answer to give. The surface carries it rather + * than the value, because there is no value to colour, and a reader + * scanning for the answer needs to see at a glance that there is not + * one. + */ +.c-eg-result--none { + background-color: $colour-pink-light; + border: 1px solid $colour-red; + + .c-eg-result__value { + @include make-text-default(); + font-weight: $font-weight-bold; + color: $colour-red; + } +}