Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions products/desktop/docs/CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ When creating reusable styled components, accept both `className?: string` and `

Default line heights are set in [packages/ui/src/styles/globals.css](../packages/ui/src/styles/globals.css). Add `leading-*` only when the component needs a non-default line height. Pair arbitrary body text sizes with `leading-snug`; pair titles with `leading-tight`.

Spinning icons go through `Spin` or `Spinner` from [packages/ui/src/primitives/Spinner.tsx](../packages/ui/src/primitives/Spinner.tsx). Never put `animate-spin` on an `<svg>`: Chromium animates SVG transforms on the main thread, so one visible spinner costs a style recalc and a layerize pass on every frame. Stop the animation (`spinning={false}`) or unmount the spinner when it is hidden behind `opacity-0`.

Do not write a `:has()` rule with `html` or `body` as the anchor and a descendant subject (`body:has(...) .thing`). Chromium re-checks such a rule after DOM mutations anywhere and restyles the whole document. Set custom properties on the anchor instead and consume them where the style applies, as the quill portal rule in `globals.css` does.

Animate `transform` and `opacity` only. Keyframes on `left`, `width`, or `height` force a layout every frame.

## Logging

Do not use `console.*` in source. Inject `ROOT_LOGGER` as `RootLogger`, then scope it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ describe("PreBaselineState", () => {
});

expect(screen.getByText("Establishing the baseline")).toBeVisible();
expect(screen.getByRole("status", { name: "Loading" })).toHaveClass(
"motion-safe:animate-spin",
"motion-reduce:animate-none",
);
expect(
screen.getByRole("status", { name: "Loading" }).parentElement,
).toHaveClass("animate-spin", "motion-reduce:animate-none");
Comment thread
charlesvien marked this conversation as resolved.
const metrics = screen.getByRole("status", {
name: "Loading autoresearch metrics",
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MagnifyingGlass } from "@phosphor-icons/react";
import type { AutoresearchRun } from "@posthog/core/autoresearch/schemas";
import { Spinner } from "@posthog/quill";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { Badge, Skeleton, Text } from "@radix-ui/themes";

export interface SessionActivity {
Expand All @@ -24,7 +25,9 @@ export function PreBaselineState({
<div className="flex items-start gap-3 rounded-md border border-blue-6 bg-blue-2 px-3 py-3">
{live && (
<span className="relative mt-0.5 size-5 shrink-0">
<Spinner className="size-5 motion-safe:animate-spin motion-reduce:animate-none" />
<Spin className="size-5 motion-reduce:animate-none">
<Spinner />
</Spin>
Comment thread
posthog[bot] marked this conversation as resolved.
</span>
)}
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
TooltipTrigger,
} from "@posthog/quill";
import type { CanvasDiagnostic } from "@posthog/shared";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { toast } from "@posthog/ui/primitives/toast";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";
Expand Down Expand Up @@ -106,7 +107,9 @@ export function CanvasBuildStatus({
className="flex items-center gap-1"
data-testid="canvas-build-active"
>
<SpinnerGapIcon size={14} className="animate-spin text-gray-9" />
<Spin className="text-gray-9">
<SpinnerGapIcon size={14} />
</Spin>
<Text size="xs" variant="muted">
{active.buildStatus === "queued" ? "Queued" : "Building"} · {elapsed}
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useCanvasChatPanelStore } from "@posthog/ui/features/canvas/stores/canv
import type { EditorHandle } from "@posthog/ui/features/message-editor/types";
import { EmbeddedSessionView } from "@posthog/ui/features/sessions/components/EmbeddedSessionView";
import { taskDetailQuery } from "@posthog/ui/features/tasks/queries";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { useQuery } from "@tanstack/react-query";
import { type Ref, useEffect, useRef } from "react";

Expand Down Expand Up @@ -165,7 +166,9 @@ function CanvasChatLoader({ taskId }: { taskId: string }) {
if (!task) {
return (
<div className="flex h-full items-center justify-center">
<SpinnerGapIcon size={18} className="animate-spin text-gray-9" />
<Spin className="text-gray-9">
<SpinnerGapIcon size={18} />
</Spin>
</div>
);
}
Expand Down Expand Up @@ -193,7 +196,9 @@ function CanvasCommentsLoader({
if (!task) {
return (
<div className="flex h-full items-center justify-center">
<SpinnerGapIcon size={18} className="animate-spin text-gray-9" />
<Spin className="text-gray-9">
<SpinnerGapIcon size={18} />
</Spin>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { useCommentsQuery } from "@posthog/ui/features/sessions/components/useCo
import { useSessionForTask } from "@posthog/ui/features/sessions/useSession";
import { taskDetailQuery } from "@posthog/ui/features/tasks/queries";
import { ResizableSidebar } from "@posthog/ui/primitives/ResizableSidebar";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { toast } from "@posthog/ui/primitives/toast";
import { track } from "@posthog/ui/shell/analytics";
import {
Expand Down Expand Up @@ -924,10 +925,9 @@ export function FreeformCanvasView({
{interactive &&
(isGenerating && effectiveTaskId ? (
<>
<SpinnerGapIcon
size={14}
className="animate-spin text-accent-9"
/>
<Spin className="text-accent-9">
<SpinnerGapIcon size={14} />
</Spin>
<Text size="1" className="text-gray-10">
Generating
</Text>
Expand Down Expand Up @@ -1293,7 +1293,9 @@ function LoadingState() {
<Empty className="h-full">
<EmptyHeader>
<EmptyMedia variant="icon">
<SpinnerGapIcon size={18} className="animate-spin text-accent-9" />
<Spin className="text-accent-9">
<SpinnerGapIcon size={18} />
</Spin>
</EmptyMedia>
<EmptyTitle>Loading canvas</EmptyTitle>
</EmptyHeader>
Expand All @@ -1314,7 +1316,9 @@ function GeneratingState({
<Empty className="h-full border-0">
<EmptyHeader>
<EmptyMedia variant="icon">
<SpinnerGapIcon size={18} className="animate-spin text-accent-9" />
<Spin className="text-accent-9">
<SpinnerGapIcon size={18} />
</Spin>
</EmptyMedia>
<EmptyTitle>Generating</EmptyTitle>
<EmptyDescription>An agent is building this canvas.</EmptyDescription>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ArrowsClockwise, WifiHigh, WifiSlash } from "@phosphor-icons/react";
import { useService } from "@posthog/di/react";
import { useConnectivity } from "@posthog/ui/hooks/useConnectivity";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { Box } from "@radix-ui/themes";
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
Expand Down Expand Up @@ -101,10 +102,9 @@ function OfflineRow({
onClick={onRetry}
className="flex shrink-0 items-center gap-1.5 rounded-2 bg-(--amber-a4) px-2 py-1 font-medium text-(--amber-11) text-[12px] transition-colors hover:bg-(--amber-a5) disabled:opacity-60"
>
<ArrowsClockwise
size={13}
className={isChecking ? "animate-spin" : undefined}
/>
<Spin spinning={isChecking}>
<ArrowsClockwise size={13} />
</Spin>
{isChecking ? "Checking…" : "Retry"}
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from "@posthog/quill";
import type { RegisteredFolder } from "@posthog/ui/features/folders/types";
import { useFolders } from "@posthog/ui/features/folders/useFolders";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { toast } from "@posthog/ui/primitives/toast";
import { FIELD_TRIGGER_CLASS } from "@posthog/ui/styles/fieldTrigger";
import { Text } from "@radix-ui/themes";
Expand Down Expand Up @@ -178,10 +179,9 @@ export function FolderPicker({
</Text>
</div>
{isOpening ? (
<CircleNotch
size={14}
className="shrink-0 animate-spin text-(--gray-9)"
/>
<Spin className="shrink-0 text-(--gray-9)">
<CircleNotch size={14} />
</Spin>
) : (
<CaretDown size={14} className="shrink-0 text-(--gray-9)" />
)}
Expand All @@ -195,7 +195,9 @@ export function FolderPicker({
{isOpening ? "Opening..." : displayValue || placeholder}
</span>
{isOpening ? (
<CircleNotch size={10} className="animate-spin text-muted-foreground" />
<Spin className="text-muted-foreground">
<CircleNotch size={10} />
</Spin>
) : (
<CaretDown size={10} weight="bold" className="text-muted-foreground" />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Spinner,
Text,
} from "@posthog/quill";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { Tooltip } from "@posthog/ui/primitives/Tooltip";
import { FIELD_TRIGGER_CLASS } from "@posthog/ui/styles/fieldTrigger";
import { defaultFilter } from "cmdk";
Expand Down Expand Up @@ -290,10 +291,9 @@ export function GitHubRepoPicker({
onRefresh();
}}
>
<ArrowClockwise
size={14}
className={isRefreshing ? "animate-spin" : undefined}
/>
<Spin spinning={isRefreshing}>
<ArrowClockwise size={14} />
</Spin>
</InputGroupButton>
</InputGroupAddon>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Check,
GitBranch,
Plus,
Spinner,
} from "@phosphor-icons/react";
import { useService } from "@posthog/di/react";
import { useHostTRPC } from "@posthog/host-router/react";
Expand All @@ -26,6 +25,7 @@ import type {
GitBusyOperation,
GitBusyState,
} from "@posthog/shared/domain-types";
import { Spin, Spinner } from "@posthog/ui/primitives/Spinner";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { type RefObject, useEffect, useMemo, useRef, useState } from "react";
import { Tooltip } from "../../../primitives/Tooltip";
Expand Down Expand Up @@ -59,7 +59,7 @@ const USE_INPUT_BRANCH_ACTION = "__use_input_branch__";
function LoadingRow({ label }: { label: string }) {
return (
<div className="flex items-center gap-1 px-2 py-1.5 text-muted-foreground text-xs">
<Spinner size={12} className="animate-spin" />
<Spinner size={12} />
{label}
</div>
);
Expand Down Expand Up @@ -386,7 +386,7 @@ export function BranchSelector({
className="min-w-0 max-w-[250px] shrink"
>
{showSpinner ? (
<Spinner size={14} className="shrink-0 animate-spin" />
<Spinner size={14} className="shrink-0" />
) : (
<GitBranch size={14} weight="regular" className="shrink-0" />
)}
Expand Down Expand Up @@ -470,10 +470,9 @@ export function BranchSelector({
onRefresh();
}}
>
<ArrowClockwise
size={14}
className={isRefreshing ? "animate-spin" : undefined}
/>
<Spin spinning={isRefreshing}>
<ArrowClockwise size={14} />
</Spin>
</InputGroupButton>
) : null}
</InputGroupAddon>
Expand Down Expand Up @@ -586,7 +585,7 @@ export function BranchSelector({
>
{cloudBranchesFetchingMore ? (
<>
<Spinner size={14} className="animate-spin" />
<Spinner size={14} />
Loading more…
</>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { SignalSourceValues } from "@posthog/ui/features/inbox/components/S
import { InboxBadge } from "@posthog/ui/features/inbox/components/utils/InboxBadge";
import { getSourceProductMeta } from "@posthog/ui/features/inbox/components/utils/source-product-icons";
import { Badge } from "@posthog/ui/primitives/Badge";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { Box, Flex, Spinner, Switch, Text } from "@radix-ui/themes";
import { type ComponentType, memo, useCallback } from "react";

Expand Down Expand Up @@ -225,10 +226,9 @@ const ResponderAgentCard = memo(function ResponderAgentCard({

{armed && agent.source === "session_replay" && status === "syncing" ? (
<Flex align="center" gap="2" className="mt-2 ml-8">
<CircleNotchIcon
size={14}
className="animate-spin text-(--accent-11)"
/>
<Spin className="text-(--accent-11)">
<CircleNotchIcon size={14} />
</Spin>
<Text className="text-(--accent-11) text-[13px]">
Session analysis run in progress…
</Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { GitHubSourceRepositoriesDialog } from "@posthog/ui/features/inbox/components/GitHubSourceRepositoriesDialog";
import { getSourceProductMeta } from "@posthog/ui/features/inbox/components/utils/source-product-icons";
import { Badge } from "@posthog/ui/primitives/Badge";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { memo, useCallback, useState } from "react";

export type SignalSourceValues = Record<ToggleableSourceProduct, boolean>;
Expand Down Expand Up @@ -317,7 +318,9 @@ function SourceRunningIndicator({
}
return (
<div className="mt-2 flex items-center gap-2">
<CircleNotchIcon size={14} className="animate-spin text-(--accent-11)" />
<Spin className="text-(--accent-11)">
<CircleNotchIcon size={14} />
</Spin>
<span className="text-(--accent-11) text-[13px]">{message}</span>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events";
import { StopCloudRunDialog } from "@posthog/ui/features/sessions/components/StopCloudRunDialog";
import { Badge } from "@posthog/ui/primitives/Badge";
import { Button } from "@posthog/ui/primitives/Button";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { toast } from "@posthog/ui/primitives/toast";
import { navigateToTaskDetail } from "@posthog/ui/router/navigationBridge";
import { track } from "@posthog/ui/shell/analytics";
Expand Down Expand Up @@ -145,11 +146,9 @@ export function LoopRunRow({
<Flex direction="column" className="min-w-0 gap-1.5">
<Flex align="center" gap="2" wrap="wrap">
<Badge color={statusColor(run.status)}>
<StatusIcon
size={10}
weight="bold"
className={cn(run.status === "in_progress" && "animate-spin")}
/>
<Spin spinning={run.status === "in_progress"}>
<StatusIcon size={10} weight="bold" />
</Spin>
{run.status.replaceAll("_", " ")}
</Badge>
<Text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CheckCircle, CircleNotch } from "@phosphor-icons/react";
import { PANEL_SHADOW } from "@posthog/ui/features/onboarding/components/onboardingStyles";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { Box, Flex, Text } from "@radix-ui/themes";
import type { ReactNode } from "react";

Expand Down Expand Up @@ -33,7 +34,9 @@ export function CliCheckPanel({
</Text>
</Flex>
{isLoading ? (
<CircleNotch size={14} className="animate-spin text-(--gray-9)" />
<Spin className="text-(--gray-9)">
<CircleNotch size={14} />
</Spin>
) : (
statusBadge
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "@phosphor-icons/react";
import { repoMatchesGitHubRepos } from "@posthog/core/onboarding/repoProvider";
import { cn } from "@posthog/quill";
import { Spin } from "@posthog/ui/primitives/Spinner";
import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities";
import { Button, Flex, Text } from "@radix-ui/themes";
import { AnimatePresence, motion } from "framer-motion";
Expand Down Expand Up @@ -149,10 +150,9 @@ export function SelectRepoStep({
transition={{ duration: 0.15 }}
>
<Flex align="center" gap="2">
<CircleNotch
size={14}
className="animate-spin text-(--gray-9)"
/>
<Spin className="text-(--gray-9)">
<CircleNotch size={14} />
</Spin>
<Text className="text-(--gray-9) text-[13px]">
Detecting repository...
</Text>
Expand Down
Loading
Loading