Skip to content

feat: trying out blobs - #17

Open
linus-jansson wants to merge 23 commits into
mainfrom
feat/new_sum
Open

feat: trying out blobs#17
linus-jansson wants to merge 23 commits into
mainfrom
feat/new_sum

Conversation

@linus-jansson

@linus-jansson linus-jansson commented Mar 29, 2025

Copy link
Copy Markdown
Owner
  • content areas will eventually be created

Summary by CodeRabbit

  • New Features

    • Launched a refreshed homepage featuring a simplified layout with dynamic card elements and an animated background effect.
    • Introduced a new footer with updated branding and quick access links.
    • Enhanced global styling with improved dark mode support.
    • Added a new BlobProvider component to manage mouse interactions and animations.
    • Introduced a new MouseBlob component for enhanced visual effects based on mouse movement.
    • Added a Card component for displaying content with a structured layout.
    • Introduced a new useMobile hook to detect mobile device usage.
    • Added a new CSS class for responsive design adjustments.
  • Removals

    • Removed the contact page, cookie consent banner, and several legacy UI elements to streamline the user experience.
    • Eliminated various components related to form handling and project display to simplify the codebase.
    • Removed several unused components and configuration files to enhance maintainability.

linus-jansson and others added 15 commits June 26, 2024 17:33
Snyk has created this PR to upgrade lucide-react from 0.379.0 to 0.381.0.

See this package in npm:
lucide-react

See this project in Snyk:
https://app.snyk.io/org/linus-jansson/project/8b9297ec-1d27-4cc2-b01d-7f163fe34cd1?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
* fix: upgrade next from 14.2.1 to 14.2.3

Snyk has created this PR to upgrade next from 14.2.1 to 14.2.3.

See this package in npm:
next

See this project in Snyk:
https://app.snyk.io/org/linus-jansson/project/8b9297ec-1d27-4cc2-b01d-7f163fe34cd1?utm_source=github&utm_medium=referral&page=upgrade-pr

* updated lockfile

---------

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: Linus Jansson <linus.jansson2@uppsala.se>
* fix: upgrade lucide-react from 0.381.0 to 0.383.0

Snyk has created this PR to upgrade lucide-react from 0.381.0 to 0.383.0.

See this package in npm:
lucide-react

See this project in Snyk:
https://app.snyk.io/org/linus-jansson/project/8b9297ec-1d27-4cc2-b01d-7f163fe34cd1?utm_source=github&utm_medium=referral&page=upgrade-pr

* bump packages

---------

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: Linus Jansson <linus.jansson2@uppsala.se>
* feat: migrate to nextjs 15
* fix: type error

---------

Co-authored-by: Linus Jansson <contact@limpan.dev>
@vercel

vercel Bot commented Mar 29, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
limpan-dev ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 29, 2025 6:39pm

@coderabbitai

coderabbitai Bot commented Mar 29, 2025

Copy link
Copy Markdown

Walkthrough

The update refactors configuration, dependencies, styling, and components throughout the project. In configuration files, properties and types have been updated (e.g., in components.json, eslint.config.mjs, next.config.ts, postcss.config.mjs, and tsconfig.json). The package manifest received numerous dependency removals, version updates, and a project name change. Several UI components, pages, and utility modules have been removed, while new modules such as a custom useMobile hook, new Blob components, and a Card component were added. Overall, the changes streamline the codebase and adjust the styling and build setups.

Changes

File(s) Change Summary
components.json Updated styling properties ("style": "new-york", "baseColor": "neutral"), emptied Tailwind config field, added new aliases (ui, lib, hooks), and introduced iconLibrary.
eslint.config.mjs, next.config.ts, postcss.config.mjs, tsconfig.json Added new ESLint configuration with Next.js support; improved Next.js config typing; replaced Tailwind CSS plugin declaration format; simplified TypeScript paths and compiler options.
package.json Renamed project; removed several dependencies and devDependencies; updated multiple package versions (e.g., class-variance-authority, lucide-react, tailwind-merge, uuid, eslint, tailwindcss); updated package manager version; and streamlined scripts.
Global styling files (globals.css) Overhauled CSS structure: replaced Tailwind directives with @import statements and added custom theming (global variables, dark mode variant, base styles); removed component-specific layers.
Component & Page Files (multiple removals) Deleted many files including contact form, cookie banner, blob page, cookies page, header, footer, project card components, numerous UI components (button, form, input, label, link, textarea), and data/utility modules (projects.json, constants, contact schema, discord, encryption, settings, utils-server).
Lib & Utility Files Removed several utility modules and constants; refactored color generation in utils.ts by replacing random generator functions with a new seeded RGB function using a hashing algorithm.
New Additions Added new features including a custom hook (useMobile), a new Footer component under Layout, Blob-related components (BlobProvider, MouseBlob with associated export), and a new Card component with its export index.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant BP as BlobProvider
    participant MB as MouseBlob

    U->>BP: Moves mouse
    BP->>BP: Updates mousePosition state
    BP->>MB: Passes current mousePosition
    MB->>MB: Animates blob based on position
    MB-->>U: Renders updated blob animation
Loading
sequenceDiagram
    participant U as User
    participant H as Home Page
    participant C as Card Component

    U->>H: Loads Home page
    H->>C: Renders Card components
    C-->>H: Returns card display
    H-->>U: Displays complete layout with social links and scroll indicator
Loading

Poem

I’m a rabbit in the code’s vast field,
Hopping through updates, sleek and real.
New features bloom like carrots bright,
Old bits vanish into the night.
I nibble bugs with joyful zeal 🐇
And leave a trail of clover in every commit.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/app/layout.tsx

Oops! Something went wrong! :(

ESLint: 9.23.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.2.4_eslint@9.23.0_jiti@2.4.2__typescript@5.8.2/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11e5c1f and ad22d50.

📒 Files selected for processing (1)
  • src/app/layout.tsx (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
src/app/layout.tsx (2)
src/lib/utils.ts (1)
  • cn (4-6)
src/components/Layout/Footer.tsx (1)
  • Footer (3-16)
🔇 Additional comments (6)
src/app/layout.tsx (6)

2-3: Good import organization for new functionality.

The added imports for Geist fonts, Analytics, Footer component, and Viewport type are well-organized and appropriate for the new functionality being introduced.

Also applies to: 6-7


9-17: Well-configured font setup.

The font configuration follows Next.js best practices by:

  • Using the Google font loader from Next.js
  • Setting up appropriate CSS variables
  • Configuring proper subsets for optimization

This will provide consistent typography throughout the application.


19-21: Good viewport configuration.

The viewport configuration with viewportFit: 'cover' helps optimize the display for devices with non-rectangular screens (like newer iPhones with notches).


23-39: Comprehensive metadata setup.

The metadata is properly configured with detailed information for SEO and social sharing. The OpenGraph properties will improve link previews when sharing the site on social media.


49-49: Good styling approach with utility classes.

The use of the cn utility function to combine the font variables with Tailwind utility classes is a clean approach for styling. The flex-col class ensures proper layout stacking for the footer component.


52-52: Footer placement enhances user experience.

The Footer component is correctly placed after the main content and before the closing body tag, ensuring it appears at the bottom of the page regardless of content height due to the flex-col class on the body.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (9)
src/hooks/use-mobile.tsx (1)

1-26: Well-implemented hook with proper cleanup

The useMobile hook is correctly implemented with proper state management, event listeners, and cleanup. The "use client" directive ensures client-side execution.

Consider these enhancements for improved flexibility and performance:

-export function useMobile() {
+export function useMobile(breakpoint = 768) {
  const [isMobile, setIsMobile] = useState(false)

  useEffect(() => {
    const checkIfMobile = () => {
-     setIsMobile(window.innerWidth < 768)
+     setIsMobile(window.innerWidth < breakpoint)
    }

    // Check on initial load
    checkIfMobile()
    
+   // Debounce function to limit execution during rapid resize
+   const debounce = (fn: Function, ms = 300) => {
+     let timeoutId: ReturnType<typeof setTimeout>;
+     return function(this: any, ...args: any[]) {
+       clearTimeout(timeoutId);
+       timeoutId = setTimeout(() => fn.apply(this, args), ms);
+     };
+   };
+   
+   const debouncedCheckIfMobile = debounce(checkIfMobile);

    // Add event listener for window resize
-   window.addEventListener("resize", checkIfMobile)
+   window.addEventListener("resize", debouncedCheckIfMobile)

    // Clean up
    return () => {
-     window.removeEventListener("resize", checkIfMobile)
+     window.removeEventListener("resize", debouncedCheckIfMobile)
    }
  }, [breakpoint])

  return isMobile
}
eslint.config.mjs (1)

1-16: Well-structured ESLint flat config implementation

The ESLint configuration correctly implements the new flat config format, properly handling ESM module paths and extending Next.js specific configurations.

Consider adding more specific rules and settings for your project:

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript"),
+ {
+   rules: {
+     // Add project-specific rules here
+     "react/no-unused-prop-types": "warn",
+     "react/self-closing-comp": "warn",
+   },
+ },
];
src/app/BlobProvider.tsx (2)

8-30: Fix empty object pattern in component props

The component is well-structured with proper event handling, state management, and cleanup. However, there's an unnecessary empty object pattern in the component declaration.

-export const BlobProvider = ({}) => {
+export const BlobProvider = () => {

This will simplify your code while maintaining the same functionality.

🧰 Tools
🪛 Biome (1.9.4)

[error] 8-8: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)


27-29: Consider adding loading state or fallback for client-side rendering

The component returns null if not mounted, which can cause layout shifts when the component finally renders. This might affect user experience, especially for the visual blob effect.

Consider adding a minimal fallback or ensuring the parent layout accounts for this initial null render to prevent layout shifts.

src/lib/utils.ts (1)

9-22: Consider clarifying that cyrb53 is non-cryptographic.

The custom 53-bit hash implementation looks correct for generating seeds. Since it’s not cryptographically secure, consider documenting its intended scope (e.g., color generation) to avoid misuse in security-related contexts.

src/components/mouse-blob.tsx (4)

12-14: Potential collisions in the date-based seed.

Using only month+day (e.g. "711") might result in repetitive colors across different years. If variety is desired, consider adding the year or an additional factor to the seed.

-const MonthDay = `${d.getMonth()}${d.getDate()}`
+const MonthDay = `${d.getFullYear()}${d.getMonth()}${d.getDate()}`

33-35: All three gradient stops use the same seed.

c1, c2, and c3 use the identical MonthDay value, producing the same color. If a more dynamic gradient is intended, consider using different seeds or offsets for each color stop.

-const c1 = useMemo(() => getSeededRGB(MonthDay), []);
-const c2 = useMemo(() => getSeededRGB(MonthDay), []);
-const c3 = useMemo(() => getSeededRGB(MonthDay), []);
+const c1 = useMemo(() => getSeededRGB(MonthDay + '1'), []);
+const c2 = useMemo(() => getSeededRGB(MonthDay + '2'), []);
+const c3 = useMemo(() => getSeededRGB(MonthDay + '3'), []);

37-148: Animation logic is well-structured but sizable.

The code effectively handles both mouse-following and autonomous movement. Consider splitting larger computations into helper functions or custom hooks for maintainability.


150-199: Path generation is detailed but heavy.

Dynamically creating a Bézier-based blob is correct for generating the shape. However, extracting calculations for path control points into a helper function might improve readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c55e74 and e2a284f.

⛔ Files ignored due to path filters (8)
  • bun.lockb is excluded by !**/bun.lockb
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/R66g1Pe.jpeg is excluded by !**/*.jpeg
  • public/bg.png is excluded by !**/*.png
  • public/file.svg is excluded by !**/*.svg
  • public/globe.svg is excluded by !**/*.svg
  • public/vercel.svg is excluded by !**/*.svg
  • public/window.svg is excluded by !**/*.svg
📒 Files selected for processing (40)
  • components.json (1 hunks)
  • eslint.config.mjs (1 hunks)
  • next.config.ts (1 hunks)
  • package.json (1 hunks)
  • postcss.config.mjs (1 hunks)
  • src/app/BlobProvider.tsx (1 hunks)
  • src/app/blob/page.tsx (0 hunks)
  • src/app/contact/contactForm.tsx (0 hunks)
  • src/app/contact/cookieBanner.tsx (0 hunks)
  • src/app/contact/page.tsx (0 hunks)
  • src/app/cookies/page.tsx (0 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (1 hunks)
  • src/app/page.tsx (1 hunks)
  • src/app/server-actions.tsx (0 hunks)
  • src/components/Cc.tsx (0 hunks)
  • src/components/blob.module.css (0 hunks)
  • src/components/blob.tsx (0 hunks)
  • src/components/footer.tsx (0 hunks)
  • src/components/header.tsx (0 hunks)
  • src/components/icons.tsx (0 hunks)
  • src/components/mouse-blob.tsx (1 hunks)
  • src/components/projectCard.tsx (0 hunks)
  • src/components/ui/button.tsx (0 hunks)
  • src/components/ui/form.tsx (0 hunks)
  • src/components/ui/input.tsx (0 hunks)
  • src/components/ui/label.tsx (0 hunks)
  • src/components/ui/link.tsx (0 hunks)
  • src/components/ui/textarea.tsx (0 hunks)
  • src/data/projects.json (0 hunks)
  • src/hooks/use-mobile.tsx (1 hunks)
  • src/lib/constants.ts (0 hunks)
  • src/lib/contactSchema.ts (0 hunks)
  • src/lib/discord.ts (0 hunks)
  • src/lib/encryption.ts (0 hunks)
  • src/lib/settings.ts (0 hunks)
  • src/lib/utils-server.ts (0 hunks)
  • src/lib/utils.ts (1 hunks)
  • tailwind.config.ts (0 hunks)
  • tsconfig.json (2 hunks)
💤 Files with no reviewable changes (27)
  • src/app/blob/page.tsx
  • tailwind.config.ts
  • src/components/Cc.tsx
  • src/components/ui/link.tsx
  • src/components/footer.tsx
  • src/components/blob.tsx
  • src/lib/encryption.ts
  • src/components/icons.tsx
  • src/components/ui/label.tsx
  • src/components/ui/textarea.tsx
  • src/app/contact/page.tsx
  • src/components/header.tsx
  • src/app/contact/contactForm.tsx
  • src/app/server-actions.tsx
  • src/lib/contactSchema.ts
  • src/components/blob.module.css
  • src/lib/utils-server.ts
  • src/data/projects.json
  • src/lib/settings.ts
  • src/lib/discord.ts
  • src/components/ui/button.tsx
  • src/components/projectCard.tsx
  • src/app/contact/cookieBanner.tsx
  • src/components/ui/form.tsx
  • src/app/cookies/page.tsx
  • src/components/ui/input.tsx
  • src/lib/constants.ts
🧰 Additional context used
🧬 Code Definitions (3)
src/components/mouse-blob.tsx (1)
src/lib/utils.ts (1)
  • getSeededRGB (24-30)
src/app/page.tsx (1)
src/app/BlobProvider.tsx (1)
  • BlobProvider (8-30)
src/app/BlobProvider.tsx (2)
src/hooks/use-mobile.tsx (1)
  • useMobile (5-26)
src/components/mouse-blob.tsx (1)
  • MouseBlob (19-215)
🪛 Biome (1.9.4)
src/app/BlobProvider.tsx

[error] 8-8: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

🔇 Additional comments (26)
postcss.config.mjs (1)

1-5: Configuration updated for Tailwind CSS v4

The change from object notation { tailwindcss: {} } to array notation ["@tailwindcss/postcss"] aligns with Tailwind CSS v4 requirements. This is the correct format for the new version.

next.config.ts (1)

1-5: Improved type safety with explicit NextConfig typing

The changes provide better type safety by explicitly importing and applying the NextConfig type. This helps catch configuration errors during development.

src/app/page.tsx (1)

1-17: Clean implementation with placeholder content

The page has been simplified with a clean, responsive layout using Tailwind CSS. The integration of the BlobProvider suggests you're adding an interactive visual element that follows the mouse cursor.

Note that the heading "Heading" and the Lorem ipsum text are placeholders that will need to be replaced with real content as mentioned in the PR description ("content areas will eventually be created").

tsconfig.json (1)

21-23:

Details

❓ Verification inconclusive

Verify path resolution after removing baseUrl

The baseUrl property has been removed while keeping the paths configuration. While modern TypeScript can often handle this, verify that path imports still work correctly throughout your application, especially with the @/* imports.


🏁 Script executed:

#!/bin/bash
# Check for any import issues in TypeScript files
grep -r "from '@/" --include="*.ts" --include="*.tsx" . | head -n 10

Length of output: 73


Action: Verify TypeScript Alias Resolution Without baseUrl

The tsconfig.json now only specifies the alias configuration:

    "paths": {
      "@/*": ["./src/*"]
    }

However, our initial search for import statements using "@/" produced no output. This might mean that there are no files using this alias—or that the search command didn’t catch them. Please manually verify that any files relying on the "@/" alias resolve correctly. To help further investigation, consider running this script:

If any usage is found, confirm that the resolved paths work as expected, especially since the baseUrl property has been removed.


🏁 Script executed:

#!/bin/bash
# Search all TypeScript files for any import statements using the '@/'
find . -type f \( -name "*.ts" -o -name "*.tsx" \) -exec grep -H "from ['\"]@/" {} \;

Length of output: 447


Action: Confirm module resolution for the '@/*' alias after baseUrl removal

The current configuration in tsconfig.json now only specifies the alias:

"paths": {
  "@/*": ["./src/*"]
}

Our repository search identified several files using the alias, for example:

  • ./src/app/BlobProvider.tsx (imports from "@/components/mouse-blob" and "@/hooks/use-mobile")
  • ./src/components/mouse-blob.tsx (imports from "@/lib/utils")

Please ensure that these imports resolve as expected without the baseUrl property. Verify by running the TypeScript compiler (e.g., tsc) and/or manual testing to catch any potential alias resolution issues.

components.json (3)

13-19: Good addition of consistent import aliases

Adding clear aliases for commonly used directories (ui, lib, hooks) is a good practice that will help maintain consistent import paths throughout the project.


20-20: Added Lucide icon library support

The addition of Lucide as the icon library is a good choice - it's lightweight, well-maintained, and has good TypeScript support.


3-10:

Details

✅ Verification successful

Updated styling configuration

You've switched from the "default" style to "new-york" and changed the base color from "slate" to "neutral". The Tailwind config path has also been emptied, which suggests you're handling Tailwind configuration differently now.


🌐 Web query:

What is the "new-york" style in shadcn/ui components?

💡 Result:

The "New York" style in shadcn/ui components is a modern, condensed design system that prioritizes sleek aesthetics and spatial efficiency. It contrasts with the Default style, offering distinct visual and functional differences:

Core Characteristics

  • Compact sizing
    Components like buttons and inputs use h-9 (36px height) instead of Default's h-10 (40px), creating a denser UI[1][3].
  • Subtle shadows
    Applies shadow-sm to interactive elements (buttons, inputs, selects) for depth[1][10].
  • Refined typography
    Card titles shrink from 1.5rem (Default) to base font size (1rem)[1].
  • Sharp focus states
    Enhanced visual indicators for accessibility, using clearer outlines[1][4].
  • Tighter radii
    Smaller border radii on components compared to Default, e.g., tabs use rounded-md vs rounded-lg[3][12].

Visual Comparison

Feature New York Style Default Style
Button Height 36px (h-9) 40px (h-10)
Shadow Usage Enabled (shadow-sm) None
Card Title Size 1rem 1.5rem
Icon Set Radix Icons Lucide Icons

Ideal Use Cases

  • Dashboard interfaces requiring information density[12].
  • Modern web apps that benefit from subtle depth cues through shadows[1].
  • Accessibility-focused projects leveraging stronger focus states[1][4].
  • Design systems aiming for a refined, contemporary aesthetic[3][7].

The style is configured during project initialization via components.json and affects all components globally[13]. While initially optimized for specific use cases, both styles remain fully customizable via CSS variables and Tailwind overrides[1][16].

Citations:


Updated Styling Configuration Verified

  • The configuration now explicitly uses the "new-york" style, which aligns with shadcn/ui’s modern, compact design system featuring refined typography, subtle shadows, and tighter component spacing.
  • Changing the base color from "slate" to "neutral" is in line with a shift toward a more neutral and customizable palette.
  • Leaving the Tailwind config path empty indicates a deliberate move to manage Tailwind settings differently—consistent with the adoption of the new design system.
src/lib/utils.ts (3)

1-1: No issues with the import statement.

Using type imports from the clsx library is straightforward and helps with type safety in TypeScript.


3-3: Empty line change detected.

No functional changes. No concerns here.


24-30: Implementation for seeded RGB looks good.

The shift-and-mask approach to derive RGB channels from the hash is straightforward and meets common color generation requirements.

src/components/mouse-blob.tsx (5)

1-2: Client-side directive confirmed.

Marking the file as a client component is appropriate for browser-specific APIs (e.g., window). No issues noted.


3-9: Interface and imports look good.

The MouseBlobProps interface is concise, and importing getSeededRGB from @/lib/utils aligns with the new color scheme approach.


15-17: Linear interpolation utility is correct.

The lerp function is standard and well-implemented.


19-32: Component initialization appears coherent.

Using refs and React state for animation is appropriate. The default values provided for followMouse, minRadius, and maxRadius are sensible.


201-216: SVG with gradient definitions is correct.

Rendering the blurred blob behind content using absolute positioning is a sound approach for decorative backgrounds.

src/app/globals.css (6)

1-2: Switching to imports for Tailwind and animations looks good.

These imports replace previous @tailwind directives, aligning with your new setup.


4-4: Use of a custom variant for dark mode is acceptable.

This approach can help maintain a consistent dark theme across deeply nested elements.


6-44: Inline theme variables improve maintainability.

Defining variables in a single @theme inline block for easier scaling is a good approach.


46-79: Global variables in :root using OKLCH color notation.

This color model can offer more predictable color transitions. Looks good.


81-113: Dark mode overrides are consistent.

Mirroring the same variable set for dark mode is aligned with theming best practices.


115-121: Global base styles apply to all elements.

Be mindful that these globally applied @apply rules can override local styling. Verify there are no conflicts with other components.

src/app/layout.tsx (2)

2-13: Font implementation looks good

Good job updating to use the official Next.js font system with both sans and mono variants of Geist. The variable naming and configuration are well structured.


25-33: Clean layout implementation

The layout implementation is clean and follows best practices by applying font variables to the body element.

Note that the previous Analytics component has been removed - make sure this is intentional if you need analytics tracking for your site.

package.json (3)

19-19: New animation dependency aligns with PR objective

Adding tw-animate-css aligns with the PR objective of "trying out blobs" as this likely provides animation capabilities for the blob implementation.


28-30:

Details

❌ Incorrect review comment

Consider version stability for core tooling

You've updated to very recent major versions of core tools:

  • ESLint v9 (released February 2024)
  • TailwindCSS v4 (currently in preview)

These versions may have compatibility issues or incomplete documentation. Consider if you need these cutting-edge versions or if more stable versions would be sufficient.


🌐 Web query:

What is the current stable version of tailwindcss?

💡 Result:

The current stable version of Tailwind CSS is v4.0.17, released 3 days ago on March 26, 2025[8]. This version includes several fixes such as resolving CLI hangs with Ruby files and other minor improvements[2][8].

Key updates in the v4.x series include:

  • Separate packages for PostCSS (@tailwindcss/postcss) and CLI (@tailwindcss/cli)[1][3]
  • Modern browser targeting (Safari 16.4+, Chrome 111+, Firefox 128+)[1][3]
  • CSS-first configuration approach replacing tailwind.config.js[6]
  • Performance improvements through the Oxide engine (now 10x faster than v3)[3]

For projects requiring older browser support, Tailwind maintains v3.4.x as a stable branch[1][4]. The v4 line has seen regular updates since its initial January 2025 release, with 17 minor/patch versions addressing various bugs and compatibility issues[2][7][8].

Citations:


Reevaluate Core Tooling Versions

Based on updated information, Tailwind CSS v4 is no longer in preview—the current stable release is v4.0.17 (released March 26, 2025). While ESLint v9 (from February 2024) is still relatively new, it appears to have matured into stability over the past year. Please verify that both versions meet your project's compatibility and stability requirements before proceeding.

Likely an incorrect or invalid review comment.


11-20:

Details

❓ Verification inconclusive

Review dependency removals

Many dependencies have been removed including:

  • @vercel/analytics - for site analytics
  • Form handling packages (react-hook-form, zod, etc.)
  • Security packages (@marsidev/react-turnstile)

Ensure these removals are intentional and align with your project's requirements. The PR title mentions "trying out blobs" but these changes appear more extensive.

Also note that React has been downgraded from 19.1.0 to ^19.0.0 - verify this is intentional.


🏁 Script executed:

#!/bin/bash
# Check which components might be using the removed dependencies
echo "Searching for potential usage of removed dependencies..."
rg -l "@vercel/analytics|react-hook-form|zod|@marsidev/react-turnstile" --glob "src/**/*.{tsx,ts}"

Length of output: 223


Below is an extended search script to double-check for any references to the removed dependencies across a broader range of file types. Please run this script and verify its output before finalizing your review.

Once you confirm that no references are found—and that the React downgrade from 19.1.0 to ^19.0.0 is intentional—please update your review accordingly.


🏁 Script executed:

#!/bin/bash
echo "Performing extended search for potential usage of removed dependencies in all relevant file types..."
rg -l '@vercel/analytics|react-hook-form|zod|@marsidev/react-turnstile' --glob "*.{js,jsx,ts,tsx}"

Length of output: 433


Dependency Changes Reviewed
The extended search did not reveal any usage of the removed dependencies in the repository. Please confirm that the React downgrade from 19.1.0 to ^19.0.0 is also intentional and that no regressions occur due to this change.

  • Verify that no components require any of the removed dependencies.
  • Double-check that the React version change aligns with project requirements.

Comment thread src/app/layout.tsx Outdated
Comment thread package.json Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/components/mouse-blob.tsx (5)

1-4: Import statements can be better organized

The imports could be better organized by grouping React imports first, followed by internal utility imports.

-"use client"
-
-import { getSeededRGB } from "@/lib/utils";
-import { useEffect, useMemo, useRef, useState } from "react"

+"use client"
+
+import { useEffect, useMemo, useRef, useState } from "react"
+import { getSeededRGB } from "@/lib/utils";

33-35: All color generation calls use the same seed

All three color generation calls use the same seed value MonthDay, which means they might generate the same or very similar colors. This could result in a poor gradient effect if the colors are too similar.

-const c1 = useMemo(() => getSeededRGB(MonthDay), []);
-const c2 = useMemo(() => getSeededRGB(MonthDay), []);
-const c3 = useMemo(() => getSeededRGB(MonthDay), []);
+const c1 = useMemo(() => getSeededRGB(MonthDay + "-1"), []);
+const c2 = useMemo(() => getSeededRGB(MonthDay + "-2"), []);
+const c3 = useMemo(() => getSeededRGB(MonthDay + "-3"), []);

67-68: Typo in function name

There's a typo in the function name updateWindowSize.

-    updateWindowSize()
+    updateWindowSize()

156-194: SVG path generation may be performance intensive

The path generation logic is recalculated on every animation frame. Consider optimizing this by:

  1. Using useMemo for calculations that depend only on certain values
  2. Reducing complexity for mobile devices where performance might be a concern

You could memoize parts of the path calculation or reduce the number of points for lower-performance devices:

-  const points = 10 // More points for more detail
+  // Adjust detail based on device capability
+  const points = windowSize.width < 768 ? 6 : 10

202-213: Consider adding aria attributes for accessibility

The blob is purely decorative, so it should be explicitly marked as such for screen readers.

-    <div ref={wrapperRef} className="pointer-events-none fixed inset-0 z-0 flex items-center justify-center opacity-75">
+    <div ref={wrapperRef} className="pointer-events-none fixed inset-0 z-0 flex items-center justify-center opacity-75" aria-hidden="true">
      <svg className="h-full w-full blur-3xl" viewBox={`0 0 ${window.innerWidth} ${window.innerHeight}`}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2a284f and 864407c.

📒 Files selected for processing (1)
  • src/components/mouse-blob.tsx (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
src/components/mouse-blob.tsx (1)
src/lib/utils.ts (1)
  • getSeededRGB (24-30)
🔇 Additional comments (4)
src/components/mouse-blob.tsx (4)

15-17: The linear interpolation function is correctly implemented

The lerp function correctly implements linear interpolation between two values based on a factor.


37-52: Responsive radius calculation is well implemented

The calculation of the base radius based on screen size is well thought out, ensuring the blob scales appropriately across different device sizes.


85-148: Animation effect is complex and well-implemented

The animation logic is comprehensive, handling both mouse-following behavior and autonomous movement with proper boundaries to ensure visibility. The code correctly uses requestAnimationFrame for smooth animations and properly cleans up resources.


19-24: Props have good default values

The component provides sensible default values for optional props, making it more flexible and easier to use.

Comment on lines +80 to +82
if (position.x === 0 && position.y === 0) {
setPosition({ x: mousePosition.x, y: mousePosition.y })
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing dependency array in effect hook

This useEffect hook initializes the position but doesn't have a dependency array, which means it will run on every render. You should add an empty dependency array to ensure it only runs once during component initialization.

-  useEffect(() => {
+  useEffect(() => {
    // Initialize position to mouse position on first render
    if (position.x === 0 && position.y === 0) {
      setPosition({ x: mousePosition.x, y: mousePosition.y })
    }
-  })
+  }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (position.x === 0 && position.y === 0) {
setPosition({ x: mousePosition.x, y: mousePosition.y })
}
useEffect(() => {
// Initialize position to mouse position on first render
if (position.x === 0 && position.y === 0) {
setPosition({ x: mousePosition.x, y: mousePosition.y })
}
}, [])

Comment on lines +150 to +152
useEffect(() => {
if (!blobRef.current || !wrapperRef.current) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Conditional check for hook dependencies

The conditional check for refs might cause issues if the component unmounts before these refs are set. Consider adding a check for component mounted state.

  useEffect(() => {
+    let isMounted = true;
    if(!blobRef.current || !wrapperRef.current) return
    
    // Rest of the effect implementation
    
    return () => {
+      isMounted = false;
      // Existing cleanup code
    }
  }, [position, rotation, time, baseRadius, windowSize])

Committable suggestion skipped: line range outside the PR's diff.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/components/mouse-blob.tsx (1)

78-83: ⚠️ Potential issue

Missing dependency array in useEffect

This useEffect hook doesn't have a dependency array, which means it will run on every render. You should add an empty dependency array to ensure it only runs once during component initialization.

-  useEffect(() => {
+  useEffect(() => {
    // Initialize position to mouse position on first render
    if (position.x === 0 && position.y === 0) {
      setPosition({ x: mousePosition.x, y: mousePosition.y })
    }
-  })
+  }, [])
🧹 Nitpick comments (5)
src/app/page.tsx (1)

20-31: Consider extracting button components

These buttons share similar styling patterns with only color differences. Consider extracting them into a reusable Button component to reduce duplication.

-        <section className="mt-4 flex flex-col gap-2">
-          <button className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300">
-            Button 1
-          </button>
-          <button className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300">
-            Button 2
-          </button>
-          <button className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300">
-            Button 3
-          </button>
-
-        </section>
+        <section className="mt-4 flex flex-col gap-2">
+          <Button color="blue">Button 1</Button>
+          <Button color="green">Button 2</Button>
+          <Button color="red">Button 3</Button>
+        </section>

You would need to create a Button component:

type ButtonProps = {
  children: React.ReactNode;
  color: 'blue' | 'green' | 'red';
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

function Button({ children, color, ...props }: ButtonProps) {
  const colorClasses = {
    blue: 'bg-blue-500 hover:bg-blue-600',
    green: 'bg-green-500 hover:bg-green-600',
    red: 'bg-red-500 hover:bg-red-600',
  };
  
  return (
    <button 
      className={`rounded ${colorClasses[color]} px-4 py-2 text-white transition duration-300`}
      {...props}
    >
      {children}
    </button>
  );
}
src/components/mouse-blob.tsx (4)

12-14: Use a more robust date seed implementation

The current date seeding approach will generate the same colors on the same day of each month, which may not provide enough variation.

-const d = new Date()
-const MonthDay = `${d.getMonth()}${d.getDate()}`
+const d = new Date()
+const MonthDay = `${d.getFullYear()}${d.getMonth()}${d.getDate()}`

This will ensure unique colors for each day across years as well.


33-35: Redundant color generation

All three color variables use the same seed, which may result in identical or very similar colors. Consider modifying the seed slightly for each color.

-const c1 = useMemo(() => getSeededRGB(MonthDay), []);
-const c2 = useMemo(() => getSeededRGB(MonthDay), []);
-const c3 = useMemo(() => getSeededRGB(MonthDay), []);
+const c1 = useMemo(() => getSeededRGB(MonthDay + "1"), []);
+const c2 = useMemo(() => getSeededRGB(MonthDay + "2"), []);
+const c3 = useMemo(() => getSeededRGB(MonthDay + "3"), []);

84-148: Complex animation logic with potential performance implications

The animation logic is well-structured but could have performance implications:

  1. It's running on every frame via requestAnimationFrame
  2. It's updating state on every frame, which can cause re-renders
  3. The dependency array includes windowSize which is an object (potential for reference equality issues)

Consider using a combination of useCallback for the animate function and destructuring windowSize in dependencies:

+ const { width, height } = windowSize;

  useEffect(() => {
+   const animate = useCallback(() => {
-   const animate = () => {
      // Animation logic...
-   }
+   }, [mousePosition, followMouse, baseRadius, width, height]);

    // Start animation
    animationRef.current = requestAnimationFrame(animate)

    // Cleanup
    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current)
      }
    }
-  }, [mousePosition, followMouse, baseRadius, windowSize])
+  }, [mousePosition, followMouse, baseRadius, width, height])

150-199: SVG path generation could benefit from memoization

The complex SVG path generation runs on every position/rotation change. Consider memoizing the path calculation:

+ const generatePath = useCallback(() => {
+   const points = 10 // More points for more detail
+   const slice = (Math.PI * 2) / points
+   
+   let path = ""
+   
+   // Path generation logic...
+   
+   return path
+ }, [position, rotation, time, baseRadius])

  useEffect(() => {
    if (!blobRef.current || !wrapperRef.current) return

-   const points = 10
-   const slice = (Math.PI * 2) / points
-
-   let path = ""
-
-   // Path generation logic...
+   const path = generatePath()

    blobRef.current.setAttribute("d", path)

    // Position the wrapper
    wrapperRef.current.style.transform = `translate3d(${position.x - windowSize.width / 2}px, ${position.y - windowSize.height / 2}px, 0)`
  }, [position, rotation, time, baseRadius, windowSize, generatePath])
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 864407c and dbbd230.

📒 Files selected for processing (2)
  • src/app/page.tsx (1 hunks)
  • src/components/mouse-blob.tsx (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
src/app/page.tsx (1)
src/app/BlobProvider.tsx (1)
  • BlobProvider (8-30)
🔇 Additional comments (8)
src/app/page.tsx (3)

1-2: Good choice of imports

Clean import structure. The BlobProvider will add the interactive blob effect to your page.


4-7: Page component looks good

The Home component is properly structured as the default export for Next.js page routing.


8-16: Replace placeholder content with real content

The heading "Heading" and Lorem ipsum text are placeholder content that should be replaced with actual content before finalizing this PR.

src/components/mouse-blob.tsx (5)

1-11: Good use of client-side directive

Using "use client" is appropriate here since this component relies on browser APIs like window events.

The interface is well-defined with appropriate optional props and defaults.


15-17: Well-implemented lerp function

The linear interpolation function is correctly implemented and will provide smooth transitions for the blob animation.


19-32: Good component structure and defaults

The component props, defaults, and state initialization are well organized. The use of refs for animation and DOM elements is appropriate.


37-56: Well-implemented responsive radius calculation

The radius calculation logic is well thought out, scaling the blob size based on screen dimensions.


58-76: Good window resize handling

The window resize event listener is properly set up and cleaned up in the useEffect hook.

Comment thread src/app/page.tsx Outdated
Comment on lines +201 to +214
return (
<div ref={wrapperRef} className="pointer-events-none fixed inset-0 z-0 flex items-center justify-center opacity-75 will-change-auto duration-500 blur-3xl">
<svg className="h-full w-full" viewBox={`0 0 ${window.innerWidth} ${window.innerHeight}`}>
<defs>
<linearGradient id="blob-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor={c1} />
<stop offset="50%" stopColor={c2} />
<stop offset="100%" stopColor={c3} />
</linearGradient>
</defs>
<path ref={blobRef} fill="url(#blob-gradient)" />
</svg>
</div>
)

@coderabbitai coderabbitai Bot Mar 29, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid direct window access in render

Using window.innerWidth and window.innerHeight directly in the render method can cause issues with server-side rendering.

- <svg className="h-full w-full" viewBox={`0 0 ${window.innerWidth} ${window.innerHeight}`}>
+ <svg className="h-full w-full" viewBox={`0 0 ${windowSize.width || 1} ${windowSize.height || 1}`}>

This uses the windowSize state that's already being tracked rather than accessing window directly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div ref={wrapperRef} className="pointer-events-none fixed inset-0 z-0 flex items-center justify-center opacity-75 will-change-auto duration-500 blur-3xl">
<svg className="h-full w-full" viewBox={`0 0 ${window.innerWidth} ${window.innerHeight}`}>
<defs>
<linearGradient id="blob-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor={c1} />
<stop offset="50%" stopColor={c2} />
<stop offset="100%" stopColor={c3} />
</linearGradient>
</defs>
<path ref={blobRef} fill="url(#blob-gradient)" />
</svg>
</div>
)
return (
<div ref={wrapperRef} className="pointer-events-none fixed inset-0 z-0 flex items-center justify-center opacity-75 will-change-auto duration-500 blur-3xl">
- <svg className="h-full w-full" viewBox={`0 0 ${window.innerWidth} ${window.innerHeight}`}>
+ <svg className="h-full w-full" viewBox={`0 0 ${windowSize.width || 1} ${windowSize.height || 1}`}>
<defs>
<linearGradient id="blob-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor={c1} />
<stop offset="50%" stopColor={c2} />
<stop offset="100%" stopColor={c3} />
</linearGradient>
</defs>
<path ref={blobRef} fill="url(#blob-gradient)" />
</svg>
</div>
)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

However, the component is "use client" so it will never render on the server

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@linus-jansson You're absolutely right! The "use client" directive explicitly marks this component for client-side rendering only, so there's no risk of SSR errors from accessing the window object directly.

I should have recognized that in my review. Thanks for pointing it out!


✏️ Learnings added
Learnt from: linus-jansson
PR: linus-jansson/limpan.dev#17
File: src/components/mouse-blob.tsx:201-214
Timestamp: 2025-03-29T17:18:24.391Z
Learning: In Next.js, components marked with "use client" directive at the top of the file are client-side only components that will never render on the server, making direct access to browser APIs like `window` safe within these components.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (11)
src/components/Layout/Footer.tsx (3)

1-2: Consider removing unnecessary empty lines at the beginning of the file.

The file starts with two empty lines which aren't necessary.


3-3: Add TypeScript type annotation to the component.

For better type safety and code documentation, consider adding a return type to the component.

-export const Footer = () => {
+export const Footer: React.FC = () => {

5-14: Footer component looks good but consider adding more semantic structure.

The footer structure and styling are clean. Consider adding some hover states for the links and ensuring the layout works well on mobile devices.

For improved accessibility and user experience, you might want to:

  1. Add hover styles to links
  2. Ensure responsive behavior on smaller screens
  3. Add a bit more descriptive text for the links
-    <footer className="py-4 mt-auto container mx-auto text-center">
+    <footer className="py-4 mt-auto container mx-auto text-center text-sm md:text-base">
src/components/card/Card.tsx (1)

4-7: Consider extending CardProps for future flexibility.

Currently, we're only taking title and body as mandatory fields. In some use cases, you might want a custom className, optional subtitle, or onClick handler for the card. Adding optional fields can make the component more reusable in the future without breaking changes.

src/app/page.tsx (2)

7-21: Use real URLs in social links.

The anchors currently use href="#". This should be replaced with actual URLs or removed if not needed to avoid confusion and potential user frustration.

-      <a href="#" className="text-gray-150 hover:text-gray-500">
+      <a href="https://github.com/your-username" className="text-gray-150 hover:text-gray-500">

66-70: Review large top margin or make it configurable.

Using mt-96 (24rem) can introduce significant vertical space, especially on smaller devices. Consider either reducing or making it a prop for a more responsive layout.

src/components/blob/BlobProvider.tsx (1)

8-8: Remove the unused empty object pattern.

Destructuring an empty object in the parameter list triggers a linter warning. Consider removing or adding actual props if needed.

-export const BlobProvider = ({}) => {
+export const BlobProvider = () => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 8-8: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

src/app/globals.css (4)

4-6: CSS Declaration Consistency:
In the html, body rule, while a trailing semicolon isn’t mandatory for the final declaration, adding one after scroll-behavior: smooth is recommended for consistency and to simplify future additions.


10-48: Theme Inline Block for Custom Properties:
The @theme inline block organizes custom properties by mapping semantic names (e.g., --color-background) to existing CSS variables. This design enhances maintainability and theming flexibility.
Consider adding inline comments or documentation within this block to clarify the purpose of these properties for future maintainers.


50-83: Global CSS Variables in :root:
The :root block defines global variables using the modern oklch() color function, which promotes precise color management. Ensure that your target browsers fully support oklch(), and if necessary, provide fallback values for environments that may not yet support it.


119-126: Base Layer Styling with Tailwind’s @apply:
The @layer base block leverages Tailwind’s @apply directive to enforce consistency for base styles across elements. The use of outline-ring/50 appears to be a custom-tailored utility; please ensure it aligns with your Tailwind configuration and is valid in your intended context.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbbd230 and e9f89f7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • package.json (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (1 hunks)
  • src/app/page.tsx (1 hunks)
  • src/components/Layout/Footer.tsx (1 hunks)
  • src/components/blob/BlobProvider.tsx (1 hunks)
  • src/components/blob/index.tsx (1 hunks)
  • src/components/blob/mouse-blob.tsx (1 hunks)
  • src/components/card/Card.tsx (1 hunks)
  • src/components/card/index.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/components/card/index.tsx
  • src/components/blob/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/layout.tsx
  • package.json
🧰 Additional context used
🧠 Learnings (1)
src/components/blob/BlobProvider.tsx (1)
Learnt from: linus-jansson
PR: linus-jansson/limpan.dev#17
File: src/components/mouse-blob.tsx:201-214
Timestamp: 2025-03-29T17:18:24.565Z
Learning: In Next.js, components marked with "use client" directive at the top of the file are client-side only components that will never render on the server, making direct access to browser APIs like `window` safe within these components.
🧬 Code Definitions (1)
src/components/blob/BlobProvider.tsx (1)
src/components/blob/mouse-blob.tsx (1)
  • MouseBlob (19-215)
🪛 Biome (1.9.4)
src/components/blob/BlobProvider.tsx

[error] 8-8: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

🔇 Additional comments (12)
src/components/card/Card.tsx (2)

9-19: Forwarded ref usage looks good.

Using React.forwardRef with the Card component is an excellent practice, allowing the parent to interact with the underlying <div> element if needed. The styling is consistent and the component structure is straightforward.


21-21: Retaining Card.displayName is beneficial.

Explicitly naming the forward-ref component improves debugging in React DevTools. Great job!

src/app/page.tsx (2)

23-33: ScrollIndicator structure is valid.

The component is straightforward and easy to maintain. Ensuring it's an independent piece fosters reusability. Good practice!


36-48: Clean layout and logical structure.

Using BlobProvider at the top followed by your main hero section is neatly organized and visually coherent. No further issues here.

src/components/blob/BlobProvider.tsx (2)

1-2: "use client" directive is correct.

Designating this file as a client-only component is suitable for code that references browser APIs (e.g., window.addEventListener).


9-29: Good approach for tracking and passing mouse coordinates.

Using state for mouse position and checking for mobile devices ensures the animated element won’t follow the cursor on mobile. Implementation looks solid.

src/components/blob/mouse-blob.tsx (3)

19-24: Parameter defaults and typed props are well-defined.

Providing default values for followMouse, minRadius, and maxRadius nicely avoids undefined behavior. Nicely done.


78-148: Performance and memory usage are well-handled with requestAnimationFrame.

The code properly cancels the animation on component unmount. This approach helps avoid memory leaks. No concerns here.


150-199: Dynamic path calculation is comprehensive.

The dynamic path logic for the blob shape is sophisticated. Great job ensuring it always stays within viewport bounds and presenting a smooth shape transition.

src/app/globals.css (3)

1-2: Review Imports:
The @import statements for Tailwind CSS and the animation library (tw-animate-css) are clear and appropriately referenced. Ensure the import paths correctly resolve based on your project structure.


8-8: Custom Variant Directive Usage:
The custom variant declaration @custom-variant dark (&:is(.dark *)); is an innovative approach to targeting dark mode elements. Please verify that your build and post-processing pipeline support this syntax to avoid unexpected parsing issues.


85-117: Dark Mode Custom Properties:
The .dark class mirrors the light theme properties with adjusted values for dark mode, including sophisticated opacity syntax (e.g., oklch(1 0 0 / 10%)). This approach is excellent for maintaining a consistent design language across themes.
Again, verify browser compatibility for both the oklch function and the opacity syntax in this context.

Comment on lines +10 to +11
<li>gh</li>
<li>mail</li>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace placeholder text with functional links.

The "gh" and "mail" list items should be actual links to GitHub and email. Consider adding proper anchor tags with href attributes.

-          <li>gh</li>
-          <li>mail</li>
+          <li><a href="https://github.com/linus-jansson" target="_blank" rel="noopener noreferrer" aria-label="GitHub profile">gh</a></li>
+          <li><a href="mailto:your-email@example.com" aria-label="Contact email">mail</a></li>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<li>gh</li>
<li>mail</li>
<li><a href="https://github.com/linus-jansson" target="_blank" rel="noopener noreferrer" aria-label="GitHub profile">gh</a></li>
<li><a href="mailto:your-email@example.com" aria-label="Contact email">mail</a></li>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/app/layout.tsx (2)

2-16: Good choice of modern fonts, but consider variable font optimization.

The switch to Geist and Geist_Mono fonts from Google is a good choice for a developer portfolio. These fonts provide a clean, modern look.

However, consider preloading only the variable font weight ranges you actually use to optimize performance. For example:

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
+ weight: ["400", "700"],
});

18-34: Comprehensive metadata setup, but OpenGraph image property needs adjustment.

The metadata is now much more comprehensive and follows SEO best practices with proper title, description, and OpenGraph properties. However, the OpenGraph images property should be an array of objects according to Next.js documentation.

openGraph: {
    type: 'website',
    siteName: 'Limpan | A Swedish Software Developer',
    title: 'Limpan | A Swedish Software Developer',
    description: 'Meet Limpan, a software developer from Sweden. I share my latest hobby projects and what I'm currently building. Take a look around!',
    url: 'https://limpan.dev/',
-   images: 'https://limpan.dev/bg.png',
+   images: [
+     {
+       url: 'https://limpan.dev/bg.png',
+       width: 1200,
+       height: 630,
+       alt: 'Limpan.dev',
+     }
+   ],
},
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9f89f7 and 758e749.

📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/app/layout.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🧬 Code Definitions (1)
src/app/layout.tsx (2)
src/lib/utils.ts (1)
  • cn (4-6)
src/components/Layout/Footer.tsx (1)
  • Footer (3-16)
🔇 Additional comments (2)
src/app/layout.tsx (2)

44-44: Good use of utility classes for responsive layout.

The body element now uses Flexbox with flex-col to create a vertical layout which allows the Footer component to be properly positioned at the bottom when combined with the mt-auto class in the Footer component.


19-20: Great job updating the placeholder metadata.

You've properly updated the generic placeholder metadata with specific values for your site. This addresses the issue mentioned in a previous review.

Comment thread src/app/layout.tsx
Comment on lines +41 to +50
return (
<html lang="en">
<body
className={cn(geistSans.variable, geistMono.variable, "antialiased scroll-smooth bg-gray-950 text-white flex flex-col")}
>
{children}
<Footer/>
</body>
<Analytics/>
</html>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix Analytics component placement for valid HTML.

The Analytics component is placed outside the body tag, which is invalid HTML. It should be placed inside the body tag as a sibling to other components.

return (
  <html lang="en">
    <body
      className={cn(geistSans.variable, geistMono.variable, "antialiased scroll-smooth bg-gray-950 text-white flex flex-col")}
    >
      {children}
      <Footer/>
+     <Analytics/>
    </body>
-   <Analytics/>
  </html>
);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<html lang="en">
<body
className={cn(geistSans.variable, geistMono.variable, "antialiased scroll-smooth bg-gray-950 text-white flex flex-col")}
>
{children}
<Footer/>
</body>
<Analytics/>
</html>
return (
<html lang="en">
<body
className={cn(geistSans.variable, geistMono.variable, "antialiased scroll-smooth bg-gray-950 text-white flex flex-col")}
>
{children}
<Footer/>
<Analytics/>
</body>
</html>
);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/app/page.tsx (2)

26-30: Add accessibility attributes to scroll indicator and remove commented code

The scroll indicator link lacks proper accessibility attributes, and there's commented-out code that should be removed.

    <div className={cn("absolute left-1/2 transform -translate-x-1/2", styles.downIndicatorPosition)}>
-      <a className="w-10 h-10 rounded-full bg-gray-800 flex items-center justify-center" href="#main">
-        {/* <div className="w-2 h-2 rounded-full bg-white animate-bounce" /> */}
+      <a className="w-10 h-10 rounded-full bg-gray-800 flex items-center justify-center" href="#main" aria-label="Scroll to main content">
        <FaArrowDown className="animate-bounce"/>
      </a>

67-70: Reduce excessive margin and improve card content

The current implementation has an excessive margin (mt-96) and all cards have identical placeholder content. Consider adjusting the margin and providing more diverse and meaningful card content.

-          <div className="mt-96 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 gap-4 transition-opacity group overflow-visible">
+          <div className="mt-24 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-2 gap-4 transition-opacity group overflow-visible">
            {Array.from({ length: 24 }, (_, i) => (
-              <Card key={i} title="title with some yes" body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatibus."/>
+              <Card 
+                key={`card-${i}`} 
+                title={`Project ${i + 1}`} 
+                body={`Description for project ${i + 1}. Replace with actual project details.`}
+              />
            ))}
          </div>

Consider generating actual content for a small number of cards instead of 24 placeholder cards, or fetching real project data from an API or data file.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 758e749 and 98da03c.

📒 Files selected for processing (2)
  • src/app/page.module.css (1 hunks)
  • src/app/page.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/app/page.module.css

Comment thread src/app/page.tsx
Comment on lines +42 to +46
<h1 className="text-7xl font-bold tracking-wider">Heading</h1>
<p className="mt-2 text-center w-2/3 h-auto text-lg text-wrap mb-4">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
voluptatibus.
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace placeholder content with meaningful text

The current heading and paragraph contain placeholder text. Replace with meaningful content that describes the purpose of the page.

-          <h1 className="text-7xl font-bold tracking-wider">Heading</h1>
-          <p className="mt-2 text-center w-2/3 h-auto text-lg text-wrap mb-4">
-            Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
-            voluptatibus.
-          </p>
+          <h1 className="text-7xl font-bold tracking-wider">Linus Jansson</h1>
+          <p className="mt-2 text-center w-2/3 h-auto text-lg text-wrap mb-4">
+            Full-stack developer passionate about creating intuitive and responsive web applications.
+          </p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h1 className="text-7xl font-bold tracking-wider">Heading</h1>
<p className="mt-2 text-center w-2/3 h-auto text-lg text-wrap mb-4">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,
voluptatibus.
</p>
<h1 className="text-7xl font-bold tracking-wider">Linus Jansson</h1>
<p className="mt-2 text-center w-2/3 h-auto text-lg text-wrap mb-4">
Full-stack developer passionate about creating intuitive and responsive web applications.
</p>

Comment thread src/app/page.tsx
Comment on lines +54 to +63
<h2 className="text-5xl font-bold tracking-wider">Massa knappar</h2>
<button className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300">
Button 1
</button>
<button className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300">
Button 2
</button>
<button className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300">
Button 3
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Standardize language and add purpose to buttons

The heading uses Swedish "Massa knappar" while the rest of the content is in English. Additionally, the buttons lack clear purpose or functionality.

-          <h2 className="text-5xl font-bold tracking-wider">Massa knappar</h2>
+          <h2 className="text-5xl font-bold tracking-wider">Projects</h2>
-          <button className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300">
-            Button 1
+          <button 
+            onClick={() => window.open('https://project1-url.com', '_blank')}
+            className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300 mx-2">
+            Project 1
           </button>
-          <button className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300">
-            Button 2
+          <button 
+            onClick={() => window.open('https://project2-url.com', '_blank')}
+            className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300 mx-2">
+            Project 2
           </button>
-          <button className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300">
-            Button 3
+          <button 
+            onClick={() => window.open('https://project3-url.com', '_blank')}
+            className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300 mx-2">
+            Project 3
           </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h2 className="text-5xl font-bold tracking-wider">Massa knappar</h2>
<button className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300">
Button 1
</button>
<button className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300">
Button 2
</button>
<button className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300">
Button 3
</button>
<h2 className="text-5xl font-bold tracking-wider">Projects</h2>
<button
onClick={() => window.open('https://project1-url.com', '_blank')}
className="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 transition duration-300 mx-2">
Project 1
</button>
<button
onClick={() => window.open('https://project2-url.com', '_blank')}
className="rounded bg-green-500 px-4 py-2 text-white hover:bg-green-600 transition duration-300 mx-2">
Project 2
</button>
<button
onClick={() => window.open('https://project3-url.com', '_blank')}
className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600 transition duration-300 mx-2">
Project 3
</button>

Comment thread src/app/page.tsx
Comment on lines +11 to +19
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaGithub />
</a>
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaEnvelope />
</a>
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaLinkedin />
</a>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add proper URLs and accessibility attributes to social links

The social media links are using placeholder "#" URLs and lack proper accessibility attributes, which could impact both functionality and accessibility.

-      <a href="#" className="text-gray-150 hover:text-gray-500">
+      <a href="https://github.com/linus-jansson" aria-label="GitHub Profile" rel="noopener noreferrer" className="text-gray-150 hover:text-gray-500">
        <FaGithub />
      </a>
-      <a href="#" className="text-gray-150 hover:text-gray-500">
+      <a href="mailto:your-email@example.com" aria-label="Email Contact" className="text-gray-150 hover:text-gray-500">
        <FaEnvelope />
      </a>
-      <a href="#" className="text-gray-150 hover:text-gray-500">
+      <a href="https://linkedin.com/in/your-profile" aria-label="LinkedIn Profile" rel="noopener noreferrer" className="text-gray-150 hover:text-gray-500">
        <FaLinkedin />
      </a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaGithub />
</a>
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaEnvelope />
</a>
<a href="#" className="text-gray-150 hover:text-gray-500">
<FaLinkedin />
</a>
<a href="https://github.com/linus-jansson" aria-label="GitHub Profile" rel="noopener noreferrer" className="text-gray-150 hover:text-gray-500">
<FaGithub />
</a>
<a href="mailto:your-email@example.com" aria-label="Email Contact" className="text-gray-150 hover:text-gray-500">
<FaEnvelope />
</a>
<a href="https://linkedin.com/in/your-profile" aria-label="LinkedIn Profile" rel="noopener noreferrer" className="text-gray-150 hover:text-gray-500">
<FaLinkedin />
</a>

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.

1 participant