Skip to content

Refresh Tailwind light theme#2

Merged
venikman merged 2 commits intomainfrom
ui-tailwind-lyra-light
Jan 15, 2026
Merged

Refresh Tailwind light theme#2
venikman merged 2 commits intomainfrom
ui-tailwind-lyra-light

Conversation

@venikman
Copy link
Copy Markdown
Owner

@venikman venikman commented Jan 15, 2026

Summary\n- add shared Tailwind theme package with Noto Sans, amber brand tokens, and no-radius defaults\n- improve light-mode readability with solid surfaces and stronger borders in the main UI and telemetry\n- remove legacy FAST assets and unused layout CSS, rebuild Tailwind output\n\n## Testing\n- npm run build:css\n- dotnet test tests/AudioSharp.App.Tests/AudioSharp.App.Tests.csproj

Summary by CodeRabbit

  • New Features

    • New responsive header/navigation and a dedicated Telemetry panel for AI usage stats
    • Redesigned home page layout with a left aside (TOC, recent captures, telemetry) and a focused content grid
  • Documentation

    • Added theme package README covering usage, fonts, and customization notes
  • Style

    • Introduced a shared design system and theme with CSS tokens and animations
    • Switched typography to Noto Sans and consolidated styling imports
    • Updated modal and component visuals to align with the theme

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 15, 2026 04:48
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 15, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a new Tailwind theme package (Noto Sans, tokens, utilities), consolidates app styling to import the theme, refactors layout/navigation to header-based responsive design, extracts telemetry UI into a new TelemetryPanel component, restructures Home into a responsive grid, and removes an inline Fast components script.

Changes

Cohort / File(s) Summary
Tailwind theme package
packages/tailwind-theme/README.md, packages/tailwind-theme/fonts.css, packages/tailwind-theme/theme.css, packages/tailwind-theme/package.json
New package providing Noto Sans import, CSS design tokens (colors, typography, radii, shadows), @layer base/utilities/animations, fonts.css/theme.css exports and package metadata.
App-wide styling
src/AudioSharp.App/Styles/app.css, src/AudioSharp.App/wwwroot/app.css
Replaced large custom CSS with imports: fonts.css, Tailwind, and theme.css; swapped prior font imports to Noto Sans and centralized theme tokens.
Layout and navigation
src/AudioSharp.App/Components/Layout/MainLayout.razor, src/AudioSharp.App/Components/Layout/MainLayout.razor.css, src/AudioSharp.App/Components/Layout/NavMenu.razor, src/AudioSharp.App/Components/Layout/NavMenu.razor.css
Restructured MainLayout and NavMenu to a header-based responsive layout; removed sidebar styling files; eliminated NavMenu telemetry UI and its lifecycle/DI code; deleted associated CSS files.
Reconnect modal styles
src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css
Replaced hard-coded colors and radii with CSS variables (surface, brand, ink); removed border-radius on modal and buttons; adjusted hover/active token usage.
Telemetry extraction
src/AudioSharp.App/Components/Layout/TelemetryPanel.razor
New Blazor component that subscribes to IAiUsageTelemetry, displays Audio/Text telemetry, progress bars, recent events, and implements IDisposable to unsubscribe.
Home page redesign
src/AudioSharp.App/Components/Pages/Home.razor
Replaced two-column hero with responsive grid: aside (branding, TOC, TelemetryPanel) and main content; swapped Fast controls for standard HTML elements; added recent captures, follow-up workflows, bundle display, and adjusted state guards.
Removed inline script
src/AudioSharp.App/Components/App.razor
Removed module script tag that loaded the Fast components library from the app head.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibbled tokens, stitched a theme so fine,
Noto Sans whispered through each design line,
NavMenu hopped up from the side to the top,
TelemetryPanel hums—no telemetry to drop,
A tail of CSS, compact and spry. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refresh Tailwind light theme' accurately describes the main change: introducing a new Tailwind theme package with updated design tokens and styling. It directly reflects the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


🧹 Recent nitpick comments
src/AudioSharp.App/Components/Pages/Home.razor (1)

528-565: Consider adding a save-in-progress guard.

SaveAsync doesn't set a processing flag, so rapid button clicks could trigger multiple save operations before the first completes. The CanSave condition remains true during the async save.

♻️ Suggested improvement
+    private bool IsSaving { get; set; }
+
-    private bool CanSave => !IsProcessing && Concerns.Count > 0 && !string.IsNullOrWhiteSpace(BundleJson);
+    private bool CanSave => !IsProcessing && !IsSaving && Concerns.Count > 0 && !string.IsNullOrWhiteSpace(BundleJson);

     private async Task SaveAsync()
     {
         if (!CanSave)
         {
             return;
         }

+        IsSaving = true;
         try
         {
             // ... existing code ...
         }
         catch (Exception ex)
         {
             // ... existing code ...
         }
+        finally
+        {
+            IsSaving = false;
+        }
     }

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce57ecf and 2e7c655.

📒 Files selected for processing (4)
  • packages/tailwind-theme/theme.css
  • src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css
  • src/AudioSharp.App/Components/Pages/Home.razor
  • src/AudioSharp.App/wwwroot/app.css
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css
  • packages/tailwind-theme/theme.css
🧰 Additional context used
🪛 Biome (2.1.2)
src/AudioSharp.App/wwwroot/app.css

[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration-color is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration-color is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (15)
src/AudioSharp.App/wwwroot/app.css (1)

1-2: LGTM – Generated Tailwind output aligns with PR objectives.

This is compiled CSS from Tailwind v4.1.18. The configuration correctly reflects the intentional design choices documented in the PR:

  • Noto Sans applied to --font-sans, --font-mono, and --font-display
  • Zero-radius overrides (--radius-card:0px, etc.)
  • Light-only theme with both :root and .dark mapping to light tokens

Regarding the static analysis warnings: The duplicate -webkit-text-decoration and -webkit-text-decoration-color properties are false positives. These vendor-prefixed duplicates are standard Tailwind output for progressive enhancement across browsers and will be regenerated on any subsequent build.

src/AudioSharp.App/Components/Pages/Home.razor (14)

1-23: LGTM!

The page directives, using statements, and dependency injections are well-organized and appropriate for the component's functionality.


26-89: LGTM!

The responsive grid layout and Table of Contents with conditional "Recent captures" entry are well-structured. Anchor links correctly correspond to section IDs.


91-168: LGTM!

The Capture section has correct button state management with appropriate disabled conditions. Two-way bindings and the InputFile component are properly configured.


170-294: LGTM!

The concerns display with conditional metadata rendering and the follow-up questions workflow are well-implemented. Button guards (CanGenerateFollowUps, CanApplyFollowUps, CanSave, CanSendToFhir) provide appropriate protection.


296-311: LGTM!

The Recent captures section correctly mirrors the conditional ToC entry and handles null subject data gracefully.


313-342: LGTM!

State properties and computed guards are well-defined with appropriate initialization and null-safe handling.


368-419: LGTM!

The StopRecordingAsync method has a proper guard at the start and sets IsRecording = false early to prevent reentrancy. The cancellation token handling and error paths are well-structured.


421-449: LGTM!

File upload handling is well-implemented with size limits, proper stream disposal, and cancellation support.


451-526: LGTM!

Both ExtractFromTranscriptAsync and ProcessAudioAsync follow a consistent pattern with proper cancellation token usage, state management, and error handling.


587-685: LGTM!

Follow-up generation and application methods follow a consistent async pattern with proper guards, cancellation handling, and state management.


687-716: LGTM!

The SendToFhirAsync method properly uses the IsSendingToFhir flag to prevent concurrent uploads and handles success/failure states appropriately.


718-734: LGTM!

The cancellation and reset methods follow the correct pattern for CancellationTokenSource lifecycle management.


751-783: LGTM!

The recording timer correctly implements the reentrancy guard at lines 770-773 as mentioned in the PR objectives. The InvokeAsync wrapper ensures the IsRecording check and StopRecordingAsync call execute on the synchronization context, providing adequate protection against race conditions in Blazor Server.


785-812: LGTM!

Helper methods are correctly implemented, and Dispose properly cleans up both CancellationTokenSource instances.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @venikman, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the application's styling by introducing a new, centralized Tailwind theme package. The primary goal is to modernize the UI, improve light-mode readability, and remove legacy styling dependencies, leading to a more consistent and maintainable visual experience across the application.

Highlights

  • New Tailwind Theme Package: Introduced a shared Tailwind theme package (@audiosharp/tailwind-theme) to centralize styling, including Noto Sans font, amber brand tokens, and a default no-radius design.
  • Improved Light-Mode Readability: Enhanced the light-mode UI with solid surfaces and stronger borders for better readability in the main application and telemetry sections.
  • Legacy Asset and CSS Removal: Removed outdated FAST assets and unused layout CSS, streamlining the codebase and rebuilding the Tailwind output for a cleaner, more efficient styling approach.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and positive refresh of the UI by creating a shared Tailwind theme package. This centralizes styling, removes legacy assets, and improves code maintainability. The new layout and component structure are well-executed. However, I've identified a critical issue in the theme's implementation where an aggressive border-radius reset breaks Tailwind's rounding utilities, which are used throughout the new UI components. I've also included a few other suggestions to improve the theme package and fix a minor visual bug in an animation.

Comment on lines +110 to +121
.rounded,
.rounded-sm,
.rounded-md,
.rounded-lg,
.rounded-xl,
.rounded-2xl,
.rounded-3xl,
.rounded-full,
.rounded-card,
.file\:rounded-full::file-selector-button {
border-radius: 0;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

These overrides force all Tailwind rounded-* utility classes to have a border-radius of 0. While the goal is a "no-radius default", this implementation is too aggressive and makes it impossible to use rounded corners with Tailwind's utility classes. Several components in this PR use classes like rounded-2xl, rounded-full, and rounded-card (e.g., in MainLayout.razor, TelemetryPanel.razor, Home.razor), but this override will render them ineffective, which is likely not the intention.

A better approach for a "no-radius default" is to rely on the CSS variables for radius that are already defined for shadcn compatibility (lines 56-62). Components can use these variables, and they can be overridden when needed.

Please remove this entire block of overrides to allow Tailwind's rounded-* utilities to work as expected.

{
"name": "@audiosharp/tailwind-theme",
"version": "0.1.0",
"private": true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The package is marked as private, which prevents it from being published to an npm registry. The README.md file mentions publishing as a usage option. If this package is intended to be published, "private": true should be removed or set to false.

Suggested change
"private": true,
"private": false,

Comment on lines +34 to +35
:root,
.dark {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The .dark selector is included with :root and is being assigned light theme variables, including color-scheme: light. This will cause incorrect styling if a dark theme is ever introduced, and it forces light-mode native UI elements (like scrollbars) even when the .dark class is applied. It's better to remove .dark from this rule-set for now to avoid confusion and future bugs. A separate .dark block should be created when the dark theme is actually implemented.

    :root {

border: 3px solid #0087ff;
opacity: 1;
border-radius: 50%;
border-radius: 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Changing border-radius to 0 for the .components-rejoining-animation div element will change the reconnect animation from an expanding circle to an expanding square. This is likely an unintended visual change. To preserve the circular ripple effect, this should be restored to 50%.

Suggested change
border-radius: 0;
border-radius: 50%;

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/AudioSharp.App/Components/Pages/Home.razor (1)

763-770: Potential reentrancy when auto-stopping recording.

When RecordingSeconds >= MaxRecordingSeconds, the timer calls StopRecordingAsync via InvokeAsync. If the user simultaneously clicks "Stop & analyze", both paths could race to stop the recording. Consider adding a guard or using a lock-free check.

Suggested guard
                 if (RecordingSeconds >= MaxRecordingSeconds)
                 {
-                    await InvokeAsync(StopRecordingAsync);
+                    await InvokeAsync(async () =>
+                    {
+                        if (IsRecording)
+                        {
+                            await StopRecordingAsync();
+                        }
+                    });
                     return;
                 }

Note: StopRecordingAsync already has an early return if !IsRecording, so this is defense-in-depth rather than a critical fix.

🧹 Nitpick comments (10)
packages/tailwind-theme/theme.css (3)

4-4: Consider using a proper monospace font for --font-mono.

Using "Noto Sans" (a proportional sans-serif) for --font-mono is unconventional. Monospace fonts should use fixed-width typefaces for proper code/preformatted text display.

Suggested fix
-    --font-mono: "Noto Sans", "Segoe UI", system-ui, sans-serif;
+    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

34-36: The .dark class applies light-mode styles, which may cause confusion.

Both :root and .dark share identical light-mode values with color-scheme: light. This means toggling the .dark class has no visual effect. If dark mode is intentionally unsupported, consider removing the .dark selector to avoid misleading future maintainers. If dark mode support is planned, the .dark block should define dark-mode overrides.


87-87: filter: blur(0px) is a no-op.

This declaration has no visual effect. If blur was intended for the decorative blobs, consider a non-zero value. Otherwise, this line can be removed.

Suggested fix
-        filter: blur(0px);
+        /* Remove or add actual blur value if decorative blur is desired */
src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css (1)

91-105: Inconsistent button state colors.

The default button background uses the new CSS variable var(--color-brand-500), but :hover and :active states still use hardcoded blue colors (#3b6ea2, #6b9ed2) that don't align with the amber brand tokens mentioned in the PR objectives.

♻️ Suggested fix
     `#components-reconnect-modal` button:hover {
-        background-color: `#3b6ea2`;
+        background-color: var(--color-brand-600, `#d97706`);
     }

     `#components-reconnect-modal` button:active {
-        background-color: `#6b9ed2`;
+        background-color: var(--color-brand-400, `#fbbf24`);
     }
src/AudioSharp.App/Components/Layout/MainLayout.razor (1)

12-20: Consider accessibility for the dismiss control.

The dismiss span uses an emoji (🗙) without semantic meaning. For screen readers and keyboard navigation, consider:

  1. Adding role="button" and aria-label="Dismiss error"
  2. Adding tabindex="0" for keyboard accessibility
♻️ Suggested improvement
-        <span class="dismiss ml-auto cursor-pointer text-brand-300">🗙</span>
+        <span class="dismiss ml-auto cursor-pointer text-brand-300" 
+              role="button" 
+              aria-label="Dismiss error" 
+              tabindex="0">🗙</span>
src/AudioSharp.App/Components/Layout/TelemetryPanel.razor (2)

68-73: Fire-and-forget pattern may swallow exceptions.

The _ = InvokeAsync(StateHasChanged) discards the Task, which means any exceptions thrown during rendering won't be observed. Consider adding error handling or using ConfigureAwait(false) with proper exception logging.

♻️ Suggested improvement
 private void OnTelemetryChanged()
 {
     Snapshot = AiUsageTelemetry.GetSnapshot();
     RecentEvents = AiUsageTelemetry.GetRecentEvents();
-    _ = InvokeAsync(StateHasChanged);
+    _ = InvokeAsync(StateHasChanged).ContinueWith(t =>
+    {
+        if (t.IsFaulted)
+        {
+            Console.Error.WriteLine($"TelemetryPanel render error: {t.Exception?.InnerException?.Message}");
+        }
+    }, TaskContinuationOptions.OnlyOnFaulted);
 }

Alternatively, if the Changed event can be made async-aware, consider making the handler async.


47-53: Consider extracting the conditional class logic.

The inline conditional class string is getting long. For readability, consider extracting to a helper method.

♻️ Suggested improvement
private static string GetEventClasses(AiTelemetryEvent telemetryEvent)
{
    var baseClasses = "mt-2 rounded-lg border border-black/20 bg-surface-soft px-3 py-2 text-[12px] text-ink-700 border-l-4";
    return telemetryEvent.Success 
        ? $"{baseClasses} border-l-emerald-400" 
        : $"{baseClasses} border-l-rose-400";
}

Then in markup:

<div class="@GetEventClasses(telemetryEvent)">
src/AudioSharp.App/Components/Layout/NavMenu.razor (1)

15-23: Consider mobile menu behavior.

The <details> element doesn't automatically close when:

  1. A link is clicked (navigating to an anchor on the same page)
  2. User clicks outside the menu

This is standard browser behavior but may feel unexpected to users. Consider adding JS to close the menu on link click or outside click, or document this as a known limitation.

If you want to auto-close on link click, you could add an onclick handler:

<a class="block py-1 transition hover:text-white" href="#capture" 
   `@onclick`="() => { /* close details via JS interop */ }">Capture</a>

Or use a simple inline approach if the parent details element has an id.

src/AudioSharp.App/Components/Pages/Home.razor (2)

687-716: Consider using a cancellable token for the FHIR upload.

SendToFhirAsync passes CancellationToken.None to UploadBundleAsync, which means the HTTP request cannot be cancelled if the user navigates away or the component is disposed. This could leave orphaned requests in flight.

Suggested improvement
     private async Task SendToFhirAsync()
     {
         if (!CanSendToFhir)
         {
             return;
         }

+        ResetProcessing();
         IsSendingToFhir = true;
         FhirStatusMessage = "Sending bundle to FHIR server...";

         try
         {
             var result = await FhirServerClient
-                .UploadBundleAsync(BundleJson, CancellationToken.None);
+                .UploadBundleAsync(BundleJson, ProcessingToken);

             FhirStatusMessage = result.Success
                 ? (string.IsNullOrWhiteSpace(result.Location)
                     ? "FHIR server accepted the bundle."
                     : $"FHIR server accepted the bundle: {result.Location}")
                 : $"FHIR server returned {result.StatusCode}.";
         }
+        catch (OperationCanceledException)
+        {
+            FhirStatusMessage = "FHIR send canceled.";
+        }
         catch (Exception ex)
         {
             FhirStatusMessage = $"FHIR send failed: {ex.Message}";
         }
         finally
         {
             IsSendingToFhir = false;
         }
     }

528-557: Consider using a cancellable token for the save operation.

Similar to the FHIR upload, SaveAsync uses CancellationToken.None for the repository call. While save operations are typically quick, using a cancellable token maintains consistency and allows cleanup on component disposal.

Suggested improvement
-            await ConcernRepository.AddAsync(record, CancellationToken.None);
+            await ConcernRepository.AddAsync(record, ProcessingToken);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd47d5 and ce57ecf.

⛔ Files ignored due to path filters (1)
  • src/AudioSharp.App/wwwroot/lib/fast/fast-components.min.js is excluded by !**/*.min.js
📒 Files selected for processing (14)
  • packages/tailwind-theme/README.md
  • packages/tailwind-theme/fonts.css
  • packages/tailwind-theme/package.json
  • packages/tailwind-theme/theme.css
  • src/AudioSharp.App/Components/App.razor
  • src/AudioSharp.App/Components/Layout/MainLayout.razor
  • src/AudioSharp.App/Components/Layout/MainLayout.razor.css
  • src/AudioSharp.App/Components/Layout/NavMenu.razor
  • src/AudioSharp.App/Components/Layout/NavMenu.razor.css
  • src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css
  • src/AudioSharp.App/Components/Layout/TelemetryPanel.razor
  • src/AudioSharp.App/Components/Pages/Home.razor
  • src/AudioSharp.App/Styles/app.css
  • src/AudioSharp.App/wwwroot/app.css
💤 Files with no reviewable changes (3)
  • src/AudioSharp.App/Components/Layout/MainLayout.razor.css
  • src/AudioSharp.App/Components/Layout/NavMenu.razor.css
  • src/AudioSharp.App/Components/App.razor
🧰 Additional context used
🪛 Biome (2.1.2)
src/AudioSharp.App/wwwroot/app.css

[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration-color is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 2-2: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

-webkit-text-decoration-color is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (15)
packages/tailwind-theme/fonts.css (1)

1-1: LGTM! Good font loading configuration.

The display=swap parameter is a best practice that prevents Flash of Invisible Text (FOIT). The selected font weights (400-700) provide good typographic flexibility.

One consideration: loading fonts from Google Fonts CDN introduces an external dependency and may have privacy implications (Google can track font requests). For applications with strict privacy requirements, consider self-hosting the font files.

src/AudioSharp.App/wwwroot/app.css (1)

1-2: Generated Tailwind output looks correct.

This is compiled CSS output from Tailwind v4.1.18. The duplicate -webkit-text-decoration properties flagged by static analysis are intentional vendor prefix fallbacks generated by Tailwind for cross-browser compatibility—this is expected behavior for generated CSS and not a concern.

Ensure this file is not manually edited; changes should be made to the source CSS files (fonts.css, theme.css) and rebuilt via npm run build:css.

packages/tailwind-theme/theme.css (1)

158-163: Good accessibility practice with prefers-reduced-motion.

Disabling animations for users who prefer reduced motion is an important accessibility consideration. Well done.

packages/tailwind-theme/package.json (1)

1-13: LGTM! Package configuration is well-structured.

The package correctly:

  • Uses private: true for an internal workspace package
  • Exposes only the intended CSS files via exports
  • Lists distributed files in the files array

Consider adding a "description" field for better discoverability in monorepo tooling.

packages/tailwind-theme/README.md (1)

1-30: LGTM! Clear and helpful documentation.

The README provides good guidance on:

  • Correct import order (fonts → tailwindcss → theme)
  • Both local and npm usage patterns
  • Key configuration notes

Consider adding a note about the animation utilities (animate-rise, animate-float) and their prefers-reduced-motion support.

src/AudioSharp.App/Styles/app.css (1)

1-3: LGTM! Clean consolidation of styles into the shared theme package.

The import order is correct: fonts first, then Tailwind base, then theme overrides. The relative path traversal (../../../packages/tailwind-theme/) appears correct for the monorepo structure.

src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css (1)

23-29: LGTM on CSS variable adoption and no-radius styling.

Good use of CSS variables with sensible fallbacks for theming consistency.

src/AudioSharp.App/Components/Layout/MainLayout.razor (1)

3-10: LGTM! Clean layout restructuring.

The top-navigation approach with NavMenu and responsive content container is well-structured. The responsive padding classes (px-5 sm:px-8 lg:px-12) provide good breakpoint scaling.

src/AudioSharp.App/Components/Layout/TelemetryPanel.razor (2)

57-66: LGTM! Proper lifecycle management.

The component correctly subscribes to the Changed event in OnInitialized and unsubscribes in Dispose. This prevents memory leaks from lingering event handlers.

Also applies to: 130-133


6-37: LGTM on the telemetry card markup.

Good use of the theme's CSS custom properties and Tailwind utility classes. The Audio/Text sections are consistently structured with progress bars providing visual feedback.

src/AudioSharp.App/Components/Layout/NavMenu.razor (1)

1-13: LGTM! Clean header-based navigation.

The refactor from sidebar with telemetry to a clean sticky header significantly simplifies this component. Good use of responsive classes (md:flex, md:hidden) for desktop/mobile switching.

src/AudioSharp.App/Components/Pages/Home.razor (4)

26-89: LGTM - Clean semantic structure for the aside and TOC.

The navigation uses proper anchor links that correspond to section IDs, and the conditional "Recent captures" item correctly depends on RecentRecords.Count. The vertical line indicator with absolute positioning is a nice visual touch.


91-168: LGTM - Capture section is well-implemented.

Button disabled states are properly tied to component state (IsRecording, IsProcessing), and the status indicator using data-state attributes for CSS-driven styling is a clean pattern. The InputFile component with audio/* accept filter is appropriate.


800-805: LGTM - Proper disposal of CancellationTokenSource instances.

The Dispose method correctly stops the recording timer and cancels/disposes the processing CTS, preventing resource leaks.


170-209: LGTM - Concern display handles optional fields gracefully.

The conditional rendering for Onset, Duration, Impact, Context, and PatientQuote properly checks for null/whitespace before rendering, avoiding empty UI elements.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refreshes the Tailwind light theme by introducing a shared theme package with standardized design tokens and migrating away from legacy FAST components to native HTML/CSS with Tailwind utilities.

Changes:

  • Created a reusable @audiosharp/tailwind-theme package with Noto Sans font, amber brand colors, and zero-radius defaults
  • Refactored all UI components to use Tailwind v4 utility classes instead of FAST Web Components
  • Restructured layout with sticky header navigation and sidebar telemetry panel for improved information architecture

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/tailwind-theme/* New shared theme package with fonts, color tokens, and base styles
src/AudioSharp.App/Styles/app.css Updated to import from theme package instead of inline styles
src/AudioSharp.App/wwwroot/app.css Rebuilt minified CSS output with new theme
src/AudioSharp.App/Components/Pages/Home.razor Refactored with Tailwind utilities and improved layout structure
src/AudioSharp.App/Components/Layout/TelemetryPanel.razor Extracted telemetry display into reusable component
src/AudioSharp.App/Components/Layout/NavMenu.razor Simplified to sticky header with native details/summary menu
src/AudioSharp.App/Components/Layout/MainLayout.razor Updated layout structure and error UI styling
src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css Updated to use theme CSS variables
src/AudioSharp.App/Components/Layout/*.css Removed legacy CSS files now handled by Tailwind
src/AudioSharp.App/Components/App.razor Removed FAST components script reference
Comments suppressed due to low confidence (1)

src/AudioSharp.App/Components/Layout/ReconnectModal.razor.css:105

  • The hover and active states for the button are still using the old blue color scheme (#3b6ea2 and #6b9ed2) instead of the new amber brand colors. These should be updated to use brand color variables or appropriate amber shades to maintain consistency with the new theme.
    #components-reconnect-modal button:hover {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active {
        background-color: #6b9ed2;
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +110 to +120
.rounded,
.rounded-sm,
.rounded-md,
.rounded-lg,
.rounded-xl,
.rounded-2xl,
.rounded-3xl,
.rounded-full,
.rounded-card,
.file\:rounded-full::file-selector-button {
border-radius: 0;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markup uses rounded utility classes like rounded-full, rounded-2xl, and rounded-card throughout, but these are all overridden to border-radius: 0 in the theme.css utilities layer (lines 110-121). While this achieves the desired "no-radius" design, it creates a maintainability concern where the class names don't reflect the actual styling. Consider either: 1) removing the rounded classes from the markup since they have no effect, or 2) using a different approach like setting all radius custom properties to 0 without overriding the utility classes, so the intent is clearer.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +21
<details class="relative md:hidden">
<summary class="cursor-pointer text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70">Menu</summary>
<div class="absolute right-0 mt-3 w-44 rounded-xl border border-white/10 bg-ink-950/95 p-3 text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70 shadow-card">
<a class="block py-1 transition hover:text-white" href="#capture">Capture</a>
<a class="block py-1 transition hover:text-white" href="#concerns">Concerns</a>
<a class="block py-1 transition hover:text-white" href="#followups">Follow-ups</a>
<a class="block py-1 transition hover:text-white" href="#bundle">FHIR bundle</a>
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mobile navigation menu using <details> and <summary> elements lacks proper ARIA attributes for improved accessibility. Consider adding aria-label="Mobile navigation menu" to the details element and ensuring the summary has clear labeling for screen reader users. Additionally, the dropdown content should have appropriate ARIA attributes to indicate it's a menu.

Suggested change
<details class="relative md:hidden">
<summary class="cursor-pointer text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70">Menu</summary>
<div class="absolute right-0 mt-3 w-44 rounded-xl border border-white/10 bg-ink-950/95 p-3 text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70 shadow-card">
<a class="block py-1 transition hover:text-white" href="#capture">Capture</a>
<a class="block py-1 transition hover:text-white" href="#concerns">Concerns</a>
<a class="block py-1 transition hover:text-white" href="#followups">Follow-ups</a>
<a class="block py-1 transition hover:text-white" href="#bundle">FHIR bundle</a>
<details class="relative md:hidden" aria-label="Mobile navigation menu">
<summary class="cursor-pointer text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70" aria-label="Open mobile navigation menu">Menu</summary>
<div class="absolute right-0 mt-3 w-44 rounded-xl border border-white/10 bg-ink-950/95 p-3 text-[12px] font-semibold uppercase tracking-[0.3em] text-white/70 shadow-card" role="menu" aria-label="Mobile navigation options">
<a class="block py-1 transition hover:text-white" href="#capture" role="menuitem">Capture</a>
<a class="block py-1 transition hover:text-white" href="#concerns" role="menuitem">Concerns</a>
<a class="block py-1 transition hover:text-white" href="#followups" role="menuitem">Follow-ups</a>
<a class="block py-1 transition hover:text-white" href="#bundle" role="menuitem">FHIR bundle</a>

Copilot uses AI. Check for mistakes.
@theme {
--font-sans: "Noto Sans", "Segoe UI", system-ui, sans-serif;
--font-display: "Noto Sans", "Segoe UI", system-ui, sans-serif;
--font-mono: "Noto Sans", "Segoe UI", system-ui, sans-serif;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three font families (sans, display, and mono) are set to "Noto Sans", which means there's no distinction between monospace and other fonts. This may be intentional for the design, but consider whether code snippets or technical content would benefit from an actual monospace font. The --font-mono variable is typically expected to provide a monospace typeface.

Suggested change
--font-mono: "Noto Sans", "Segoe UI", system-ui, sans-serif;
--font-mono: "JetBrains Mono", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

Copilot uses AI. Check for mistakes.
@venikman
Copy link
Copy Markdown
Owner Author

@codex

@claude

review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@venikman
Copy link
Copy Markdown
Owner Author

Addressed the actionable feedback in 2e7c655:\n- added a guard in the recording timer before calling StopRecordingAsync to avoid reentrancy\n- updated reconnect modal hover/active to use brand tokens\n- removed the no-op blur(0px) from the theme\n\nIntentional choices for this PR:\n- keeping --font-mono as Noto Sans to preserve the single-font requirement\n- :root and .dark both map to light tokens (light-only theme for now)\n- zero-radius overrides are intentional to match the provided style\n\nTests: npm run build:css; dotnet test tests/AudioSharp.App.Tests/AudioSharp.App.Tests.csproj

@venikman venikman merged commit 0c1f6e9 into main Jan 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants