Commits
the scale lived in @theme as --text-heading-*, --text-paragraph-* and
--text-monospace-*, so it generated text-heading-3, text-paragraph-small and so
on. tailwind-merge classes anything shaped like that as a colour, so inside a
tv() whose variants set text colours it dropped the size as soon as a colour
followed — which, in those definitions, is always. $lib/tv.ts existed only to
re-register the twelve names as font sizes, and every tv() in the app had to
import from there rather than from tailwind-variants to pick that up.
naming them typography-* sidesteps the collision instead of patching it: the
classes match no tailwind-merge group at all, so nothing can read them as
colours and the shim is gone. they're @utility blocks rather than theme tokens
now, since --text-* is precisely the namespace that spells its utilities text-*.
font-size comes first in each block on purpose. tailwind sorts a custom utility
into the bucket of its first declared property, which keeps typography-* ahead
of every font-weight, leading-* and tracking-* utility in the sheet, so the call
sites pairing typography-heading-4 with leading-8 font-normal still resolve the
way they did.
this does give up size-against-size merging: two typography-* classes in one
string no longer collapse, so a size passed in through class= sits next to the
component's own and the stylesheet order picks the winner. nothing does that
today — Typography takes a variant prop for it — and one classGroups entry
brings the merging back if it ever comes up.
Signed-off-by: eti <eti@eti.tf>
a joined group's segments each drew their own 1px top/bottom border in the same
token as the group's own, and the two lines landed adjacent rather than
overlapping, so the selected tab read as a 2px edge with the ::before inset
shadow stacking a third band under it. the -my-px meant to pull them together
never applied: items-center left the segments at their natural height instead of
stretching, so the margins only shrank the flex line the group had already
outgrown.
follows the appview's .btn-group instead — the group is the only thing drawing a
horizontal border, its segments take border-y-0 and stretch to fill the inner
box. the x seams are unchanged, still one divider each via -ml-px.
the recessed surface also needed dark:bg-background-canvas: subtle sits below
default in light (#f9fafb vs #fff) but above it in dark (#374151 vs #1f2937), so
an unselected segment came out lighter than the selected one. canvas (#111827)
is the appview's gray-900.
Signed-off-by: eti <eti@eti.tf>
the icon prop had no coverage, so nothing showed the gap, the icon's size step
at lg, or how a pressed icon sinks with its label. adds left, right, icon-only,
per-size and inset-shadow icon stories, plus an iconSide control.
Signed-off-by: eti <eti@eti.tf>
the content wrapper had no transition, so the label jumped its 2px while the
::before shadow faded over 150ms — two halves of one press moving at different
speeds. transition-transform duration-150 ease-in-out matches the shadow
exactly: both resolve to 0.15s cubic-bezier(0.4, 0, 0.2, 1).
Signed-off-by: eti <eti@eti.tf>
the raised variants moved the whole button down 2px on :active, so a pressed
button nudged its own box and anything laid out against it. the depth now reads
purely from the ::before shadow flip plus the label sinking: the translate moved
onto a wrapper span around the content, driven by active:*:translate-y-0.5.
the wrapper takes gap-[inherit] so the size variants' gap still applies across
the extra level, and geometry is unchanged — the sizes, loading and inset-shadow
stories measure identical button rects before and after.
Signed-off-by: eti <eti@eti.tf>
min-h-64 was on the wrapper div, but the native resize handle only ever
resizes the textarea itself, so dragging down shrank the textarea to its own
min-h-24 (96px) while the wrapper stayed at 256px — the handle detached from
the bottom border and left ~160px of dead space inside the field.
the floor now lives on the textarea, two pixels short of 16rem so the
wrapper's border keeps the outer box at exactly the exported
textareaMinHeight. rows can't do this job: it sets the resting height, not a
limit, so the handle drags straight past it.
measured in storybook with a real pointer drag on the handle: the textarea
clamps at 254px inside a 256px wrapper, matching MarkdownEditor's preview
pane, and growing upward is unaffected.
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: eti <eti@eti.tf>
Signed-off-by: eti <eti@eti.tf>
every non-base font size was a raw tailwind utility, so the design system's
scale was documented in Typography.svelte and app.css but only actually used by
Typography itself. the raw sizes now map onto the tokens:
text-xs -> text-paragraph-small (12px, exact)
text-base -> text-paragraph-large (16px, exact)
text-xl -> text-heading-4 (20px, exact)
text-2xl -> text-heading-3 (24px, exact)
text-3xl -> text-heading-2 (30px, exact)
font-mono text-xs -> text-monospace-small
text-sm is left alone: it is 14px, which is the base body size, and the base
size is out of scope.
text-lg (18px) is not on the scale, so each one snapped by hierarchy: the
subsection h2s in repo/new and the settings tabs go down to paragraph-large so
they stay below their paragraph-large h1s, while the profile display name and
the repo title go up to heading-4 so they stay above the body copy under them.
the heading tokens also carry a weight, so anything that was rendering at 400
keeps an explicit font-normal — the marketing lede, the login tagline, the issue
title and the repo title row. adopting the heading weights there is a design
call, not a refactor, so it's left for later.
verified computed sizes and weights are unchanged on the marketing home, login,
repo and issues pages, and read the storybook renders for the primitives.
Signed-off-by: eti <eti@eti.tf>
the design-system type utilities are text-heading-3, text-paragraph-small and so
on, which tailwind-merge reads as text-<colour> rather than as font sizes. that
means any tv() definition whose variants set a text colour silently drops its own
size: tv({ base: 'text-paragraph-small text-foreground-muted' }) resolves to just
the colour.
/tv exports a createTV instance that registers the tokens as font sizes, so
the size and the colour both survive while Tailwind's own text-sm/text-lg
merging keeps working. every component that used tv now imports it from there.
User.svelte is the one holdout, since it has unrelated uncommitted work in it.
Signed-off-by: eti <eti@eti.tf>
the star and fork controls hand-rolled their own joined-group wrapper and then
forced their buttons to min-h-7.5 to fit inside a max-h-8 box, so they came out
shorter than every other button on the platform. both now use ButtonGroup, which
does the radius collapsing and border overlap already, and the buttons keep
their own size, and the signed-out stars link becomes a Button instead of an
anchor styled to look like one.
measured on the repo header story: the stars control and the fork pair are both
34px now, where the pair used to be 36px and its segments 30px.
Signed-off-by: eti <eti@eti.tf>
the preview pane had its own min-height (min-h-40, or min-h-24 from the comment
editor) while the write tab took the textarea's, so toggling tabs resized the
whole editor. the pane now takes the field's own height and inset from the
exported textareaMinHeight, which leaves nothing to drift, and previewClass —
which only existed to set that height — is gone.
measured on the editor and reply stories: both tabs render a 256px pane at the
same offset.
Signed-off-by: eti <eti@eti.tf>
a joined group is a tab switcher, but the selected segment was the plain default
variant, which reads as the hovered state of an unselected one, and the group
itself drew nothing.
per figma (design system 605:3859): the group owns the outer border and a
recessed background-subtle surface, the selected segment is default + its inset
shadow so it reads as a raised card, and unselected segments are ghost with the
default foreground rather than ghost's muted one. each segment's own border is
pulled onto the group's (-m-px) so the selected segment's edge doubles as the
divider and unselected segments show no divider at all.
segmentProps() carries the recipe so the call sites stop repeating the variant
ternary. verified pixel for pixel against the figma render: 36px tall, #e5e7eb
border on all four edges, #ffffff on the selected segment against #f9fafb on the
unselected, a 1px divider at the selected segment's edge, and the inset shadow
two rows above the bottom border.
ghost's own foreground is still muted where it's used outside a group, which
figma disagrees with; that's a wider change, left alone here.
Signed-off-by: eti <eti@eti.tf>
the commits, branches and tags panels paired 16px primary lines with 12px
metadata, which read as an afterthought next to them. move the metadata to
text-paragraph-regular (14px, the base body size) so each panel keeps a 16/14
hierarchy instead of jumping 16/12.
Signed-off-by: eti <eti@eti.tf>
max-h-64 capped the field; min-h-64 gives it a comfortable starting height and
lets it grow. the text inset also moves from the wrapper onto the textarea, so
the scrollbar and the resize handle sit flush against the border rather than
floating inside the wrapper's padding — the icon slots and the loading spinner
now carry their own inset instead.
Signed-off-by: eti <eti@eti.tf>
Inter's ascent/descent split isn't symmetric within the line box, so the
mathematically centred label sat visibly high — measured 7.25px of ink above
versus 10px below on 'Closed'. nudging the label alone by 1.5px balances it
without moving the icon or changing the pill's 28px height.
Signed-off-by: eti <eti@eti.tf>
the shadow in the issue list sits on each card rather than the list container.
swap it for border-border-default, matching the readme and the other panels, and
leave the gapped stack alone so the list still reads like the appview's.
Signed-off-by: eti <eti@eti.tf>
same as the repo toolbar: the field and the clear control were raw elements
reimplementing the primitives' border, padding, focus ring and hover states. the
clear button keeps its absolute placement since Input has no interactive icon
slot, and pr-9 only applies while it's on screen.
Signed-off-by: eti <eti@eti.tf>
the toolbar hand-rolled a bordered input with an absolutely positioned icon,
duplicating what Input already does. route it through Input with iconLeft and
drop the h-8 on the form, which was fighting Input's own min-h-9.
Signed-off-by: eti <eti@eti.tf>
the connector grew rightward from left-4, so its 2px width put its centre one
pixel right of the 32px avatar column it's meant to track. -translate-x-1/2
centres it on that same reference point.
Signed-off-by: eti <eti@eti.tf>
the four-step 'generate a key, add it to settings, set your remote, push' walk
through is only useful to an owner without a key on their account. owners who
already have one now get the quick-setup commands instead, in the shape github
and codeberg use: create-and-push, or push an existing repository.
key presence comes from sh.tangled.publicKey.countKeys, fetched lazily and only
for the owner; a failed lookup falls back to the key-generation instructions
since those are the safe ones to show. the new hasSshKey prop overrides the
lookup so both variants are storyable without a backend.
Signed-off-by: eti <eti@eti.tf>
the breadcrumb pill used a uniform p-2, leaving the path cramped against its
border. px-4 py-2 matches the other single-row bordered bars in the repo view
(LanguageBar, the readme header).
Signed-off-by: eti <eti@eti.tf>
the compare link was a hand-rolled anchor with its own text size and colour.
route it through Button with the ghost variant so it picks up the shared sizing,
hover and focus treatment.
Signed-off-by: eti <eti@eti.tf>
the readme container lifted off the canvas with shadow-sm while every sibling
panel in the repo view uses a plain border. use border-border-default instead so
the cards read consistently.
Signed-off-by: eti <eti@eti.tf>
same reasoning as the comment editor: cancelling a draft isn't destructive, so
drop the danger colouring and use the plain default variant.
Signed-off-by: eti <eti@eti.tf>
the cancel action in the comment editor was a ghost button with hand-rolled
foreground-danger overrides, which read as a destructive action. cancelling a
draft isn't destructive, so it now uses the plain default variant.
Signed-off-by: eti <eti@eti.tf>
also fix up timeline styles
Signed-off-by: oppiliappan <me@oppi.li>
We don't need both in most of the cases.
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: dawn <dawn@tangled.org>
shuttle doesn't compile on macos, so exclude it
Signed-off-by: Seongmin Lee <git@boltless.me>
Signed-off-by: Seongmin Lee <git@boltless.me>
- top level comments with no replies: these just vanish
- top level comments with replies: shows "This comment was deleted"
- replies: just vanish
Signed-off-by: oppiliappan <me@oppi.li>
Signed-off-by: dawn <dawn@tangled.org>
Signed-off-by: dawn <dawn@tangled.org>
Signed-off-by: dawn <dawn@tangled.org>
the scale lived in @theme as --text-heading-*, --text-paragraph-* and
--text-monospace-*, so it generated text-heading-3, text-paragraph-small and so
on. tailwind-merge classes anything shaped like that as a colour, so inside a
tv() whose variants set text colours it dropped the size as soon as a colour
followed — which, in those definitions, is always. $lib/tv.ts existed only to
re-register the twelve names as font sizes, and every tv() in the app had to
import from there rather than from tailwind-variants to pick that up.
naming them typography-* sidesteps the collision instead of patching it: the
classes match no tailwind-merge group at all, so nothing can read them as
colours and the shim is gone. they're @utility blocks rather than theme tokens
now, since --text-* is precisely the namespace that spells its utilities text-*.
font-size comes first in each block on purpose. tailwind sorts a custom utility
into the bucket of its first declared property, which keeps typography-* ahead
of every font-weight, leading-* and tracking-* utility in the sheet, so the call
sites pairing typography-heading-4 with leading-8 font-normal still resolve the
way they did.
this does give up size-against-size merging: two typography-* classes in one
string no longer collapse, so a size passed in through class= sits next to the
component's own and the stylesheet order picks the winner. nothing does that
today — Typography takes a variant prop for it — and one classGroups entry
brings the merging back if it ever comes up.
Signed-off-by: eti <eti@eti.tf>
a joined group's segments each drew their own 1px top/bottom border in the same
token as the group's own, and the two lines landed adjacent rather than
overlapping, so the selected tab read as a 2px edge with the ::before inset
shadow stacking a third band under it. the -my-px meant to pull them together
never applied: items-center left the segments at their natural height instead of
stretching, so the margins only shrank the flex line the group had already
outgrown.
follows the appview's .btn-group instead — the group is the only thing drawing a
horizontal border, its segments take border-y-0 and stretch to fill the inner
box. the x seams are unchanged, still one divider each via -ml-px.
the recessed surface also needed dark:bg-background-canvas: subtle sits below
default in light (#f9fafb vs #fff) but above it in dark (#374151 vs #1f2937), so
an unselected segment came out lighter than the selected one. canvas (#111827)
is the appview's gray-900.
Signed-off-by: eti <eti@eti.tf>
the content wrapper had no transition, so the label jumped its 2px while the
::before shadow faded over 150ms — two halves of one press moving at different
speeds. transition-transform duration-150 ease-in-out matches the shadow
exactly: both resolve to 0.15s cubic-bezier(0.4, 0, 0.2, 1).
Signed-off-by: eti <eti@eti.tf>
the raised variants moved the whole button down 2px on :active, so a pressed
button nudged its own box and anything laid out against it. the depth now reads
purely from the ::before shadow flip plus the label sinking: the translate moved
onto a wrapper span around the content, driven by active:*:translate-y-0.5.
the wrapper takes gap-[inherit] so the size variants' gap still applies across
the extra level, and geometry is unchanged — the sizes, loading and inset-shadow
stories measure identical button rects before and after.
Signed-off-by: eti <eti@eti.tf>
min-h-64 was on the wrapper div, but the native resize handle only ever
resizes the textarea itself, so dragging down shrank the textarea to its own
min-h-24 (96px) while the wrapper stayed at 256px — the handle detached from
the bottom border and left ~160px of dead space inside the field.
the floor now lives on the textarea, two pixels short of 16rem so the
wrapper's border keeps the outer box at exactly the exported
textareaMinHeight. rows can't do this job: it sets the resting height, not a
limit, so the handle drags straight past it.
measured in storybook with a real pointer drag on the handle: the textarea
clamps at 254px inside a 256px wrapper, matching MarkdownEditor's preview
pane, and growing upward is unaffected.
Signed-off-by: eti <eti@eti.tf>
every non-base font size was a raw tailwind utility, so the design system's
scale was documented in Typography.svelte and app.css but only actually used by
Typography itself. the raw sizes now map onto the tokens:
text-xs -> text-paragraph-small (12px, exact)
text-base -> text-paragraph-large (16px, exact)
text-xl -> text-heading-4 (20px, exact)
text-2xl -> text-heading-3 (24px, exact)
text-3xl -> text-heading-2 (30px, exact)
font-mono text-xs -> text-monospace-small
text-sm is left alone: it is 14px, which is the base body size, and the base
size is out of scope.
text-lg (18px) is not on the scale, so each one snapped by hierarchy: the
subsection h2s in repo/new and the settings tabs go down to paragraph-large so
they stay below their paragraph-large h1s, while the profile display name and
the repo title go up to heading-4 so they stay above the body copy under them.
the heading tokens also carry a weight, so anything that was rendering at 400
keeps an explicit font-normal — the marketing lede, the login tagline, the issue
title and the repo title row. adopting the heading weights there is a design
call, not a refactor, so it's left for later.
verified computed sizes and weights are unchanged on the marketing home, login,
repo and issues pages, and read the storybook renders for the primitives.
Signed-off-by: eti <eti@eti.tf>
the design-system type utilities are text-heading-3, text-paragraph-small and so
on, which tailwind-merge reads as text-<colour> rather than as font sizes. that
means any tv() definition whose variants set a text colour silently drops its own
size: tv({ base: 'text-paragraph-small text-foreground-muted' }) resolves to just
the colour.
/tv exports a createTV instance that registers the tokens as font sizes, so
the size and the colour both survive while Tailwind's own text-sm/text-lg
merging keeps working. every component that used tv now imports it from there.
User.svelte is the one holdout, since it has unrelated uncommitted work in it.
Signed-off-by: eti <eti@eti.tf>
the star and fork controls hand-rolled their own joined-group wrapper and then
forced their buttons to min-h-7.5 to fit inside a max-h-8 box, so they came out
shorter than every other button on the platform. both now use ButtonGroup, which
does the radius collapsing and border overlap already, and the buttons keep
their own size, and the signed-out stars link becomes a Button instead of an
anchor styled to look like one.
measured on the repo header story: the stars control and the fork pair are both
34px now, where the pair used to be 36px and its segments 30px.
Signed-off-by: eti <eti@eti.tf>
the preview pane had its own min-height (min-h-40, or min-h-24 from the comment
editor) while the write tab took the textarea's, so toggling tabs resized the
whole editor. the pane now takes the field's own height and inset from the
exported textareaMinHeight, which leaves nothing to drift, and previewClass —
which only existed to set that height — is gone.
measured on the editor and reply stories: both tabs render a 256px pane at the
same offset.
Signed-off-by: eti <eti@eti.tf>
a joined group is a tab switcher, but the selected segment was the plain default
variant, which reads as the hovered state of an unselected one, and the group
itself drew nothing.
per figma (design system 605:3859): the group owns the outer border and a
recessed background-subtle surface, the selected segment is default + its inset
shadow so it reads as a raised card, and unselected segments are ghost with the
default foreground rather than ghost's muted one. each segment's own border is
pulled onto the group's (-m-px) so the selected segment's edge doubles as the
divider and unselected segments show no divider at all.
segmentProps() carries the recipe so the call sites stop repeating the variant
ternary. verified pixel for pixel against the figma render: 36px tall, #e5e7eb
border on all four edges, #ffffff on the selected segment against #f9fafb on the
unselected, a 1px divider at the selected segment's edge, and the inset shadow
two rows above the bottom border.
ghost's own foreground is still muted where it's used outside a group, which
figma disagrees with; that's a wider change, left alone here.
Signed-off-by: eti <eti@eti.tf>
max-h-64 capped the field; min-h-64 gives it a comfortable starting height and
lets it grow. the text inset also moves from the wrapper onto the textarea, so
the scrollbar and the resize handle sit flush against the border rather than
floating inside the wrapper's padding — the icon slots and the loading spinner
now carry their own inset instead.
Signed-off-by: eti <eti@eti.tf>
Inter's ascent/descent split isn't symmetric within the line box, so the
mathematically centred label sat visibly high — measured 7.25px of ink above
versus 10px below on 'Closed'. nudging the label alone by 1.5px balances it
without moving the icon or changing the pill's 28px height.
Signed-off-by: eti <eti@eti.tf>
same as the repo toolbar: the field and the clear control were raw elements
reimplementing the primitives' border, padding, focus ring and hover states. the
clear button keeps its absolute placement since Input has no interactive icon
slot, and pr-9 only applies while it's on screen.
Signed-off-by: eti <eti@eti.tf>
the four-step 'generate a key, add it to settings, set your remote, push' walk
through is only useful to an owner without a key on their account. owners who
already have one now get the quick-setup commands instead, in the shape github
and codeberg use: create-and-push, or push an existing repository.
key presence comes from sh.tangled.publicKey.countKeys, fetched lazily and only
for the owner; a failed lookup falls back to the key-generation instructions
since those are the safe ones to show. the new hasSshKey prop overrides the
lookup so both variants are storyable without a backend.
Signed-off-by: eti <eti@eti.tf>