diff --git a/assets/scss/common/_custom.scss b/assets/scss/common/_custom.scss index fa59c3b..2021501 100644 --- a/assets/scss/common/_custom.scss +++ b/assets/scss/common/_custom.scss @@ -723,14 +723,19 @@ $map-pulse-offsets: top: 50%; left: 100%; transform: translateY(-50%); - // 13rem where the map has room for it, and no wider than the room the mark actually - // has where it does not. `--map-room` is set per mark by the generated stylesheet: + // Between 10rem and 13rem, sized to its longest line — the place name or a handle — + // and never wider than the room the mark actually has. Every card used to be 13rem + // flat, which left "cr7258" with 100px of empty card beside it: mostly background, and + // that is what made these read as oversized. `max-content` costs nothing here — the + // card is absolutely positioned, so it has no siblings to push around. + // + // `--map-room` is set per mark by the generated stylesheet: // the percentage of the map's width between this dot and the edge the card opens // towards, and `1cqw` is one percent of that width (see `.worldmap-hits`). The // 0.25rem is slack, so the widest cards stop just short of the map's edge rather // than exactly on it. // - // The fixed width is declared first and deliberately kept: it is what a browser + // The flat 13rem cap is declared first and deliberately kept: it is what a browser // without container query units falls back to, which is where every card was before // this line. Only narrow screens are affected either way — at 1440 a card in the // middle of the map has some 460px of room, so the `min()` picks 13rem and nothing @@ -740,18 +745,46 @@ $map-pulse-offsets: // Interpolated because Sass reads a bare `min()` as its own function and rejects a // `calc()` of units it cannot resolve; wrapped like this it is passed through as the // CSS it is. - width: 13rem; - width: #{"min(13rem, calc(var(--map-room, 100) * 1cqw - 0.25rem))"}; - padding: 0.625rem 0.75rem; + width: max-content; + // A floor as well as a cap, or shrink-to-fit goes too far the other way: "cr7258" made + // a 95px card, and a panel that narrow reads as a tooltip clipped rather than as a list + // of people. 10rem is the width of the longest thing any card holds today — "Amsterdam, + // Netherlands" — so in practice the cards are one width, and the content sizing is what + // keeps a longer handle than that from being truncated rather than what varies them. + // + // Capped by the room the mark has, exactly as the maximum is: `min-width` beats + // `max-width` in the cascade, so a bare 10rem floor would have overridden the cap below + // and put the narrow-screen overflow straight back. + min-width: 10rem; + min-width: #{"min(10rem, calc(var(--map-room, 100) * 1cqw - 0.25rem))"}; + max-width: 13rem; + max-width: #{"min(13rem, calc(var(--map-room, 100) * 1cqw - 0.25rem))"}; + padding: 0.625rem 0.6875rem; text-align: left; // Opaque: it sits on the map, and a tint would leave coastlines running through // the faces. background: var(--bs-body-bg, #fff); border: 1px solid $map-land; - border-radius: 0.5rem; - box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.08); + // Rounded to match the project cards below the map rather than to a value of its + // own: 0.5rem here against their 0.75rem read as a different family of box on the + // same page. + border-radius: 0.75rem; + // Two shadows rather than one. The wide soft one lifts the card off the land; the + // tight dark one under it is what gives it an edge to sit on, which a single 24px + // blur cannot do at any alpha — turned up it greys the map, turned down the card + // floats without landing. + box-shadow: + 0 0.75rem 2rem rgba(0, 0, 0, 0.09), + 0 0.125rem 0.375rem rgba(0, 0, 0, 0.04); // Text sized against the page, not the map: this is html, so unlike the city // labels in the svg it does not shrink with the viewBox. + // + // 13px rather than the 14px this was. A card is 187px wide at its widest and holds ten + // rows at its fullest, and 14px in it was body copy in a panel the size of a tooltip: + // the handles ran to the edge and the card read as a page fragment dropped on the map. + // A step down is what makes it a label — and it buys the rows the breathing space + // below, at no cost in legibility for a handle a reader is scanning rather than + // reading. font-size: 0.8125rem; line-height: 1.4; opacity: 0; @@ -770,12 +803,48 @@ $map-pulse-offsets: // it, the neighbour needs one more move of the mouse to answer. Only ever a // neighbour of the city you are already reading, and only while you are reading it. pointer-events: auto; + + // A caret pointing back at the dot, on the card's own vertical centre — which is the + // dot's centre, because that is what the card is positioned from. It is the one thing + // that says which mark a card belongs to, and it earns its place on the cluster + // around the Pearl River Delta, where a card can float over three other dots and + // otherwise names its city without pointing at it. + // + // A rotated square with two borders, the classic recipe: the corner made by + // `border-left` and `border-bottom` faces left once the square is turned 45°. Half of + // it hangs outside the card, and its own background covers the length of the card's + // border it crosses. + &::before { + content: ""; + position: absolute; + top: 50%; + left: -0.3125rem; + width: 0.5625rem; + height: 0.5625rem; + background: var(--bs-body-bg, #fff); + border-left: 1px solid $map-land; + border-bottom: 1px solid $map-land; + border-bottom-left-radius: 0.0625rem; + transform: translateY(-50%) rotate(45deg); + } } -// East of the middle, the card opens the other way — see the template. +// East of the middle, the card opens the other way — see the template. The caret turns +// with it: the other two borders make the corner that faces right. .worldmap-hit-left .worldmap-city { left: auto; right: 100%; + + &::before { + left: auto; + right: -0.3125rem; + border-left: 0; + border-bottom: 0; + border-top: 1px solid $map-land; + border-right: 1px solid $map-land; + border-bottom-left-radius: 0; + border-top-right-radius: 0.0625rem; + } } .worldmap-hit:hover .worldmap-city, @@ -787,10 +856,27 @@ $map-pulse-offsets: visibility 0s; } +// The place, on one line and in one ink, with a hairline under it — the same hairline the +// legend rules its rows with, so the card and the list beside the map are ruled by the +// same line. +// +// The city and the country were two tones here for a while, the country in the lighter +// ink. It read wrong: the comma belongs to neither half, so it took the city's weight and +// the line broke as "Shanghai," + "China" rather than as one place name. A place name is +// one thing, so it gets one format. +// +// The same weight as the handles under it, not the semibold this was: the rule is what +// separates the head from the list, and with a line already doing that the bold was a +// second, louder way of saying it — on a 144px card that made the place name the loudest +// thing in a panel that exists to show the people. Its position and the rule are enough. .worldmap-city-head { - margin: 0 0 0.5rem; - font-weight: 600; - line-height: 1.35; + margin: 0 0 0.4375rem; + padding-bottom: 0.4375rem; + border-bottom: 1px solid $map-land; + line-height: 1.3; + // A hair of tracking on the one line that is a name rather than a handle, and the only + // thing here that is read as words. + letter-spacing: 0.01em; } .worldmap-city-people { @@ -798,49 +884,66 @@ $map-pulse-offsets: padding-left: 0; list-style: none; - li { - padding: 0.125rem 0; - } - // The link is the row, not a word in it: the face and the handle are one target, and // the flexbox that used to be the `li` lives here so the target is the whole line - // rather than 13px of text. + // rather than 14px of text. a { display: flex; align-items: center; gap: 0.5rem; + // Out into the card's own padding, so the highlight below is a band across the card + // rather than a pill floating inside it — and back again as padding, so the text + // still starts where the heading's does. + margin: 0 -0.375rem; + // 4px above and below each face rather than 3. A 20px circle with 3px of air read as + // a column of faces touching each other; the pair of changes — smaller face, wider + // gap — is what turns the stack into a list of people. + padding: 0.25rem 0.375rem; + border-radius: 0.5rem; // Ink, not link colour. Ten orange handles inside a small white card over a map of // orange dots would have been the loudest thing in the section, and every row here // is a link — so nothing is distinguished by colouring them all. What marks them is - // the underline on hover and the cursor, which is what a row this size can carry. + // the row lighting up under the pointer, which is what a row this size can carry. color: inherit; text-decoration: none; + transition: background-color 0.12s ease; + // The mark's own colour at a tenth: the row a pointer is on belongs to the map's + // palette, and a grey band inside an orange-dotted card read as a disabled row. Kept + // to a tint rather than the 22% the halo uses, because the handle has to stay + // readable on top of it. &:hover, &:focus-visible { color: inherit; - text-decoration: underline; + background: rgba($map-mark, 0.1); } // Keyboard reaches these: the card opens on `:focus-within`, so tabbing into it is - // how it is read without a pointer, and each row has to say which one it is on. + // how it is read without a pointer, and each row has to say which one it is on. The + // tint alone would not — it is the same one the pointer leaves behind. &:focus-visible { outline: 2px solid $map-mark; - outline-offset: 2px; - border-radius: 0.25rem; + outline-offset: -1px; } } } +// 20px, not the 24px this started at. Ten of them stacked at 24px was a column of faces +// with the handles as captions; at 20 the face and its handle are the same size of thing, +// which is what a row of a list is. The image is still requested at `s=64`, so it is a +// 20px circle drawn from three times the pixels on any screen. .worldmap-avatar { flex: 0 0 auto; - width: 24px; - height: 24px; + width: 20px; + height: 20px; border-radius: 50%; // Hotlinked from avatars.githubusercontent.com, which the site's own CSP allows // (`img-src`). A missing or slow avatar leaves a grey circle rather than a gap // that moves the handle beside it. background: $map-land; + // A hairline ring, so a pale or white-cornered avatar still reads as a circle against + // the card. `box-shadow` rather than `border`, which would eat a pixel of the face. + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.06); } .worldmap-city-login { @@ -859,13 +962,15 @@ $map-pulse-offsets: display: flex; align-items: center; gap: 0.5rem; - margin: 0.125rem 0 0; - padding: 0.125rem 0; + // The same vertical rhythm as the rows above, so the ellipsis lands on the beat the + // faces are on. + margin: 0; + padding: 0.25rem 0; font-size: 0.75rem; color: $map-text; } -// Standing in for the avatar that would have been here: same 24px circle, the map's own +// Standing in for the avatar that would have been here: same 20px circle, the map's own // land as its fill, so the column of faces continues to the end of the card. .worldmap-city-more-icon { flex: 0 0 auto; diff --git a/layouts/_default/home.html b/layouts/_default/home.html index d2dd674..5bc5158 100644 --- a/layouts/_default/home.html +++ b/layouts/_default/home.html @@ -169,7 +169,20 @@

Contributors

{{/* The place, and only the place. The count was here — "Shanghai, China — 6 contributors" — and it said twice what the card already shows: the faces below are the count, and the dot's - size is what carries it on the map. */}} + size is what carries it on the map. + + `.label` whole, in one format on one line. It was split on the + comma for a while so the country could carry the lighter ink, + and that put the comma on the city's side of the split, in the + city's weight: the line read "Shanghai," and then "China" + instead of reading "Shanghai, China". + + `.label` rather than `.short` plus `.country`, which are also + here: `.label` is the one that carries the abbreviations the + map is drawn with — "USA" and "UK" rather than "United States" + and "United Kingdom" — and those are what fit on a line 13rem + wide. A country-level mark like India is a label with no comma + in it, and needs no case of its own. */}}

{{ .label }}