This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

improved tab styling

Signed-off-by: oppiliappan <me@oppi.li>

author
oppiliappan
committer
dawn
date (Jul 31, 2026, 10:54 PM +0300) commit c1b47a47 parent 3d68fe33 change-id wwztlppm
+129 -29
+1 -1
web/src/lib/components/profile/StringCard.svelte
··· 7 7 </script> 8 8 9 9 <div 10 - class="flex min-h-32 flex-col gap-1 rounded border border-border-default bg-background-default px-6 py-4 shadow-sm" 10 + class="flex min-h-32 flex-col gap-1 rounded border border-border-default bg-background-default px-6 py-4 shadow-xs" 11 11 > 12 12 <div class="flex items-center justify-between font-medium text-foreground-default"> 13 13 <div class="mr-2 flex min-w-0 flex-1 items-center">
+1 -1
web/src/lib/components/profile/tabs/StarredTab.svelte
··· 13 13 <RepoCard repo={star.repo} /> 14 14 {:else} 15 15 <div 16 - class="flex items-center gap-2 rounded border border-border-default bg-background-default px-6 py-4 shadow-sm" 16 + class="flex items-center gap-2 rounded border border-border-default bg-background-default px-6 py-4 shadow-xs" 17 17 > 18 18 <a 19 19 href={resolve(`/${star.ownerHandle}/strings/${star.rkey}` as "/")}
+1 -1
web/src/lib/components/repo/Readme.svelte
··· 10 10 let { filename, contents, html = null }: Props = $props(); 11 11 </script> 12 12 13 - <div class="mt-4 w-full overflow-hidden rounded bg-background-default shadow-sm"> 13 + <div class="mt-4 w-full overflow-hidden rounded bg-background-default shadow-xs border border-border-default"> 14 14 <div class="border-b border-border-default px-4 py-2"> 15 15 <span class="flex items-center gap-2"> 16 16 <FileText class="size-4 text-foreground-subtle" aria-hidden="true" />
+1 -1
web/src/lib/components/repo/RepoCard.svelte
··· 24 24 </script> 25 25 26 26 <div 27 - class="flex min-h-32 flex-col gap-1 rounded border border-border-default bg-background-default px-6 py-4 shadow-sm" 27 + class="flex min-h-32 flex-col gap-1 rounded border border-border-default bg-background-default px-6 py-4" 28 28 > 29 29 <div class="flex items-start justify-between font-medium text-foreground-default"> 30 30 <div class="mt-1 mr-2 flex min-w-0 flex-1 items-center">
+3 -4
web/src/lib/components/repo/RepoIndexView.svelte
··· 12 12 import Readme from "./Readme.svelte"; 13 13 import RepoToolbar from "./RepoToolbar.svelte"; 14 14 import TagList from "./TagList.svelte"; 15 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 15 16 import type { loadRepoIndex } from "$lib/api/repoIndex"; 16 17 import type { RepoInfo } from "./types"; 17 18 ··· 28 29 const refsCapped = $derived(data.refs.capped); 29 30 </script> 30 31 31 - <section 32 - class="relative mx-auto w-full rounded bg-background-default px-6 py-4 text-foreground-default" 33 - > 32 + <TabPanel class="relative mx-auto w-full"> 34 33 {#if data.needsUpgrade} 35 34 <div class="flex h-96 items-center justify-center text-center text-foreground-danger"> 36 35 <div> ··· 133 132 </div> 134 133 </div> 135 134 {/if} 136 - </section> 135 + </TabPanel> 137 136 138 137 {#if data.readme} 139 138 <Readme filename={data.readme.filename} contents={data.readme.contents} html={data.readmeHtml} />
+1 -1
web/src/lib/components/repo/issues/IssueCard.svelte
··· 18 18 ); 19 19 </script> 20 20 21 - <div class="rounded bg-background-default px-6 py-4 shadow-sm"> 21 + <div class="rounded bg-background-default p-4 shadow-xs border border-border-default"> 22 22 <div class="pb-2"> 23 23 <a {href} class="text-foreground-default no-underline"> 24 24 <span class="hover:underline">{issue.title}</span>
+1 -1
web/src/lib/components/shell/Footer.svelte
··· 335 335 336 336 {#if variant === "minimal"} 337 337 <footer 338 - class="w-full border-t border-border-subtle bg-background-default px-6 py-4 pb-[calc(env(safe-area-inset-bottom)+1rem)]" 338 + class="w-full border-t border-border-default bg-background-default px-6 py-4 pb-[calc(env(safe-area-inset-bottom)+1rem)]" 339 339 > 340 340 <div 341 341 class="mx-auto flex max-w-screen-lg flex-wrap items-center justify-center gap-x-4 gap-y-2 text-sm text-foreground-subtle"
+59
web/src/lib/components/ui/TabPanel.stories.svelte
··· 1 + <script module lang="ts"> 2 + import { defineMeta } from "@storybook/addon-svelte-csf"; 3 + import TabPanel from "./TabPanel.svelte"; 4 + import Tabs from "./Tabs.svelte"; 5 + import SquareChartGantt from "$icon/square-chart-gantt"; 6 + import CircleDot from "$icon/circle-dot"; 7 + import GitPullRequest from "$icon/git-pull-request"; 8 + 9 + const tabs = [ 10 + { id: "overview", label: "Overview", icon: SquareChartGantt, href: "#" }, 11 + { id: "issues", label: "Issues", icon: CircleDot, count: 7, href: "#" }, 12 + { id: "pulls", label: "Pulls", icon: GitPullRequest, href: "#" } 13 + ]; 14 + 15 + const { Story } = defineMeta({ 16 + title: "UI/TabPanel", 17 + component: TabPanel, 18 + tags: ["autodocs"] 19 + }); 20 + </script> 21 + 22 + <Story name="Default" asChild> 23 + <div class="mx-auto max-w-screen-lg"> 24 + <Tabs {tabs} active="overview" label="example sections" /> 25 + <TabPanel> 26 + <p>The active tab's bottom edge merges into this panel — one continuous outline.</p> 27 + </TabPanel> 28 + </div> 29 + </Story> 30 + 31 + <Story name="Second tab active" asChild> 32 + <div class="mx-auto max-w-screen-lg"> 33 + <Tabs {tabs} active="issues" label="example sections" /> 34 + <TabPanel> 35 + <p>"Issues" is active now, and the seam moves with it.</p> 36 + </TabPanel> 37 + </div> 38 + </Story> 39 + 40 + <Story name="Custom padding" asChild> 41 + <div class="mx-auto max-w-screen-lg"> 42 + <Tabs {tabs} active="overview" label="example sections" /> 43 + <TabPanel class="py-6"> 44 + <p>Roomier vertical padding (py-6) for form-heavy pages.</p> 45 + </TabPanel> 46 + </div> 47 + </Story> 48 + 49 + <Story name="Unpadded grid" asChild> 50 + <div class="mx-auto max-w-screen-lg"> 51 + <Tabs {tabs} active="overview" label="example sections" /> 52 + <TabPanel as="div" padded={false} class="grid md:grid-cols-3 md:gap-4 md:p-6"> 53 + <div class="p-6 md:p-0">Sidebar</div> 54 + <div class="border-t border-border-default p-6 md:col-span-2 md:border-t-0 md:p-0"> 55 + Main content laid out by the caller. 56 + </div> 57 + </TabPanel> 58 + </div> 59 + </Story>
+39
web/src/lib/components/ui/TabPanel.svelte
··· 1 + <script module lang="ts"> 2 + import { tv } from "tailwind-variants"; 3 + 4 + // the surface that docks directly beneath a <Tabs> bar. it pulls itself up 1px 5 + // so its top border tucks behind the bar, where the active tab's raised, opaque 6 + // background hides the seam — producing the connected "folder tab" look. keep it 7 + // flush against the tab bar (no margin between them) for the join to line up. 8 + export const tabPanel = tv({ 9 + base: "-mt-px rounded border border-border-default bg-background-default text-foreground-default", 10 + variants: { 11 + // the common surface padding; turn off to lay out children yourself 12 + padded: { 13 + true: "px-6 py-4", 14 + false: "" 15 + } 16 + }, 17 + defaultVariants: { 18 + padded: true 19 + } 20 + }); 21 + </script> 22 + 23 + <script lang="ts"> 24 + import type { Snippet } from "svelte"; 25 + 26 + interface Props { 27 + // tag to render as; a semantic <section> by default 28 + as?: string; 29 + padded?: boolean; 30 + class?: string; 31 + children: Snippet; 32 + } 33 + 34 + let { as = "section", padded = true, class: className, children }: Props = $props(); 35 + </script> 36 + 37 + <svelte:element this={as} class={tabPanel({ padded, class: className })}> 38 + {@render children()} 39 + </svelte:element>
+4 -4
web/src/lib/components/ui/Tabs.svelte
··· 26 26 const navClass = $derived( 27 27 vertical 28 28 ? "h-fit divide-y divide-border-default overflow-hidden rounded border border-border-default" 29 - : "flex w-full overflow-x-auto overflow-y-hidden pl-4" 29 + : "relative z-10 flex w-full overflow-x-auto overflow-y-hidden pl-4" 30 30 ); 31 31 32 32 const itemClass = (isActive: boolean) => ··· 36 36 ? "bg-background-default text-foreground-default dark:bg-background-inset" 37 37 : "bg-background-inset text-foreground-muted hover:text-foreground-default dark:bg-background-default" 38 38 }` 39 - : `relative mr-1 flex items-center rounded-t px-4 py-1 whitespace-nowrap text-foreground-default no-underline hover:no-underline ${ 39 + : `relative mr-1 flex items-center rounded-t px-4 pt-1 pb-[5px] whitespace-nowrap text-foreground-default no-underline hover:no-underline ${ 40 40 isActive 41 - ? "-mb-px bg-background-default [-webkit-text-stroke:0.3px_currentColor]" 42 - : "hover:bg-background-inset" 41 + ? "border border-b-0 border-border-default bg-background-default [-webkit-text-stroke:0.3px_currentColor]" 42 + : "border border-b-0 border-transparent hover:bg-background-muted hover:dark:bg-background-subtle/50" 43 43 }`; 44 44 </script> 45 45
+1 -1
web/src/routes/+error.svelte
··· 42 42 </svelte:head> 43 43 44 44 <section class="flex min-h-[60vh] flex-col items-center justify-center text-center"> 45 - <div class="mx-auto max-w-lg rounded-lg bg-background-default p-8 shadow-sm"> 45 + <div class="mx-auto max-w-lg rounded-lg bg-background-default p-8 shadow-xs"> 46 46 <div class="mb-6"> 47 47 <div 48 48 class={`mx-auto mb-4 flex size-16 items-center justify-center rounded-full ${copy.tone}`}
+1 -1
web/src/routes/+layout.svelte
··· 32 32 33 33 <div 34 34 class={isAuthShell 35 - ? "flex min-h-screen flex-col bg-background-canvas text-foreground-default" 35 + ? "flex min-h-screen flex-col bg-background-default text-foreground-default" 36 36 : isMarketingShell 37 37 ? "flex min-h-screen flex-col bg-gradient-to-b from-background-default to-background-canvas text-foreground-default" 38 38 : "flex min-h-screen flex-col bg-background-canvas text-foreground-default"}
+3 -2
web/src/routes/[handle]/+layout.svelte
··· 5 5 import EmptyState from "$lib/components/ui/EmptyState.svelte"; 6 6 import ProfileCard from "$lib/components/profile/ProfileCard.svelte"; 7 7 import ProfileTabs from "$lib/components/profile/ProfileTabs.svelte"; 8 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 8 9 9 10 let { children, data } = $props(); 10 11 ··· 29 30 <EmptyState message={`${data.identity.handle} hasn't joined Tangled yet.`} /> 30 31 {:else} 31 32 <ProfileTabs handle={data.identity.handle} active={activeTab} {counts} /> 32 - <div class="grid rounded bg-background-default shadow-sm md:grid-cols-11 md:gap-4 md:p-6"> 33 + <TabPanel as="div" padded={false} class="grid md:grid-cols-11 md:gap-4 md:p-6"> 33 34 <aside class="p-6 md:col-span-3 md:p-0"> 34 35 <ProfileCard 35 36 identity={data.identity} ··· 42 43 <div class="min-w-0 border-t border-border-default p-6 md:col-span-8 md:border-t-0 md:p-0"> 43 44 {@render children()} 44 45 </div> 45 - </div> 46 + </TabPanel> 46 47 {/if} 47 48 </section>
+3 -2
web/src/routes/[handle]/[repo]/issues/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import IssueList from "$lib/components/repo/issues/IssueList.svelte"; 3 3 import IssueToolbar from "$lib/components/repo/issues/IssueToolbar.svelte"; 4 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 4 5 5 6 let { data } = $props(); 6 7 </script> 7 8 8 - <section class="rounded bg-background-default px-6 py-4 text-foreground-default shadow-sm"> 9 + <TabPanel padded={false} class="p-4"> 9 10 <IssueToolbar 10 11 ownerHandle={data.repo.ownerHandle} 11 12 repoName={data.repo.name} ··· 13 14 openCount={data.openCount} 14 15 closedCount={data.closedCount} 15 16 /> 16 - </section> 17 + </TabPanel> 17 18 18 19 <div class="mt-2"> 19 20 <IssueList ownerHandle={data.repo.ownerHandle} repoName={data.repo.name} issues={data.issues} />
+3 -2
web/src/routes/[handle]/[repo]/issues/[aturi]/+page.svelte
··· 6 6 import CommentList from "$lib/components/comment/CommentList.svelte"; 7 7 import CommentBox from "$lib/components/comment/CommentBox.svelte"; 8 8 import SignupPrompt from "$lib/components/ui/SignupPrompt.svelte"; 9 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 9 10 import IssueForm from "$lib/components/repo/issues/IssueForm.svelte"; 10 11 import ErrorAlert from "$lib/components/ui/Error.svelte"; 11 12 import Pencil from "$icon/pencil"; ··· 119 120 <div class="grid grid-cols-1 gap-4 lg:grid-cols-10"> 120 121 <!-- conversation --> 121 122 <div class="lg:col-span-8"> 122 - <section class="rounded bg-background-default px-6 py-4 text-foreground-default shadow-sm"> 123 + <TabPanel> 123 124 {#if editing} 124 125 <IssueForm 125 126 mode="edit" ··· 148 149 </div> 149 150 {/if} 150 151 {/if} 151 - </section> 152 + </TabPanel> 152 153 153 154 <div class="mt-4 flex flex-col gap-4"> 154 155 <CommentList
+3 -2
web/src/routes/[handle]/[repo]/issues/new/+page.svelte
··· 3 3 import { page } from "$app/state"; 4 4 import { resolve } from "$app/paths"; 5 5 import IssueForm from "$lib/components/repo/issues/IssueForm.svelte"; 6 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 6 7 7 8 let { data } = $props(); 8 9 ··· 29 30 <title>New issue · {data.repo.ownerHandle}/{data.repo.name} · Tangled</title> 30 31 </svelte:head> 31 32 32 - <section class="mt-2 rounded bg-background-default px-6 py-6 text-foreground-default shadow-sm"> 33 + <TabPanel class="py-6"> 33 34 <h1 class="mb-4 text-xl font-bold text-foreground-default">Create a new issue</h1> 34 35 <IssueForm 35 36 {markup} ··· 37 38 onsaved={handleSaved} 38 39 oncancel={handleCancel} 39 40 /> 40 - </section> 41 + </TabPanel>
+3 -4
web/src/routes/[handle]/[repo]/tree/[ref]/[...path]/+page.svelte
··· 3 3 import LastCommitPanel from "$lib/components/repo/LastCommitPanel.svelte"; 4 4 import Readme from "$lib/components/repo/Readme.svelte"; 5 5 import TreeHeader from "$lib/components/repo/TreeHeader.svelte"; 6 + import TabPanel from "$lib/components/ui/TabPanel.svelte"; 6 7 7 8 let { data } = $props(); 8 9 9 10 const repo = $derived(data.repo); 10 11 </script> 11 12 12 - <section 13 - class="relative mx-auto w-full rounded bg-background-default px-6 py-4 text-foreground-default" 14 - > 13 + <TabPanel class="relative mx-auto w-full"> 15 14 <TreeHeader 16 15 ownerHandle={repo.ownerHandle} 17 16 repoName={repo.name} ··· 33 32 path={data.path} 34 33 withMessage 35 34 /> 36 - </section> 35 + </TabPanel> 37 36 38 37 {#if data.readme} 39 38 <Readme filename={data.readme.filename} contents={data.readme.contents} html={data.readmeHtml} />
+1 -1
web/src/routes/login/+page.svelte
··· 98 98 99 99 {#if auth.error} 100 100 <div 101 - class="my-2 flex gap-2 rounded border border-border-danger bg-background-danger-subtle px-3 py-2 text-foreground-danger drop-shadow-sm" 101 + class="my-2 flex gap-2 rounded border border-border-danger bg-background-danger-subtle px-3 py-2 text-foreground-danger shadow-xs" 102 102 > 103 103 <span class="py-1" aria-hidden="true"><CircleAlert class="size-4" /></span> 104 104 <div>