diff --git a/package-lock.json b/package-lock.json index 18fa3fb..8829090 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^5.0.1", + "@radix-ui/react-accordion": "^1.2.8", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-slot": "^1.2.0", "@t3-oss/env-nextjs": "^0.12.0", @@ -883,6 +884,99 @@ "node": ">=12.4.0" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.8.tgz", + "integrity": "sha512-c7OKBvO36PfQIUGIjj1Wko0hH937pYFU2tR5zbIJDUsmTzHoZVHHt4bmb7OOJbzTaWJtVELKWojBHa7OcnUHmQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collapsible": "1.1.8", + "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.8.tgz", + "integrity": "sha512-hxEsLvK9WxIAPyxdDRULL4hcaSjMZCfP7fHB0Z1uUnDoDBat1Zh46hwYfa69DeZAbJrPckjf0AGAtEZyvDyJbw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", + "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", @@ -898,6 +992,54 @@ } } }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-label": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.4.tgz", @@ -921,6 +1063,30 @@ } } }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-primitive": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", @@ -962,6 +1128,58 @@ } } }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", diff --git a/package.json b/package.json index 240f75b..1a3789b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.0.1", + "@radix-ui/react-accordion": "^1.2.8", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-slot": "^1.2.0", "@t3-oss/env-nextjs": "^0.12.0", diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d2ab6a6..0dac805 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,8 +4,10 @@ import { type Metadata } from "next"; import { Geist } from "next/font/google"; export const metadata: Metadata = { - title: "Create T3 App", - description: "Generated by create-t3-app", + title: + "FIRE Calculator - Plan Your Financial Independence & Early Retirement", + description: + "Calculate your FIRE number, estimate your retirement age, and plan your path to financial independence with this comprehensive FIRE calculator.", icons: [{ rel: "icon", url: "/favicon.ico" }], }; @@ -18,7 +20,7 @@ export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( - + {children} ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 6770123..841ac15 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,14 +1,348 @@ import FireCalculatorForm from "./components/FireCalculatorForm"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; export default function HomePage() { return ( -
+

FIRE Calculator

+ + {/* Added SEO Content Sections */} +
+
+

What is FIRE?

+

+ FIRE stands for "Financial Independence, Retire Early." + It's a movement focused on aggressive saving and investing to + build a large enough portfolio that the returns can cover living + expenses indefinitely. Achieving FIRE means you are no longer + dependent on traditional employment to fund your lifestyle, giving + you the freedom to pursue passions, travel, or simply enjoy life + without the need for a regular paycheck. +

+

+ The core principle often involves saving a high percentage of income + (sometimes 50% or more) and investing it wisely, typically in + low-cost index funds. The target amount, often called the "FIRE + number," is usually calculated as 25 times your desired annual + spending, based on the 4% safe withdrawal rate rule. +

+
+ +
+

How This Calculator Works

+

+ This calculator helps you estimate your path to FIRE based on your + current financial situation and future projections. Here's a + breakdown of the inputs: +

+
    +
  • + Starting Capital: The total amount you currently + have invested. +
  • +
  • + Monthly Savings: The amount you consistently save + and invest each month. +
  • +
  • + Current Age: Your current age in years. +
  • +
  • + Expected Annual Growth Rate (%): The average + annual return you expect from your investments (after fees, before + inflation). +
  • +
  • + Desired Monthly Allowance (Today's Value):{" "} + How much you want to be able to spend each month in retirement, in + today's money value. +
  • +
  • + Annual Inflation Rate (%): The expected average + rate at which the cost of living will increase. +
  • +
  • + Life Expectancy (Age): The age until which you + want your funds to last. +
  • +
+

+ The calculator simulates your investment growth year by year, + factoring in monthly contributions, compound growth, and + inflation's effect on your target allowance. It then determines + the age at which your accumulated capital is sufficient to sustain + your desired, inflation-adjusted monthly allowance throughout your + expected retirement years until your specified life expectancy. It + estimates your "FIRE Number" (the capital needed at + retirement) and the age you might reach it. +

+
+ +
+

+ Frequently Asked Questions (FAQ) +

+ + + + What is the 4% rule? + + + The 4% rule is a guideline suggesting that you can safely + withdraw 4% of your investment portfolio's value in your + first year of retirement, and then adjust that amount for + inflation each subsequent year, with a high probability of your + money lasting for at least 30 years. This calculator uses a more + dynamic simulation based on your life expectancy but is related + to this concept. + + + + + + Is the Expected Growth Rate realistic? + + + Historically, diversified stock market investments have returned + around 7-10% annually over the long term, before inflation. A + rate of 7% (after fees) is often used as a reasonable estimate, + but past performance doesn't guarantee future results. + It's crucial to choose a rate you feel comfortable with and + understand the associated risks. + + + + + + How does inflation impact my FIRE number? + + + Inflation erodes the purchasing power of money over time. Your + desired monthly allowance needs to increase each year just to + maintain the same standard of living. This calculator accounts + for this by adjusting your target allowance upwards based on the + inflation rate you provide, ensuring the calculated FIRE number + supports your desired lifestyle in future dollars. + + + + + + Can I really retire early? + + + Retiring significantly earlier than traditional retirement age + is possible but requires discipline, a high savings rate, and + consistent investment growth. The feasibility depends heavily on + your income, expenses, savings habits, and investment returns. + Use this calculator as a tool for planning and motivation, but + remember it provides estimates based on your inputs. + + + + + + What does FIRE stand for? + + + FIRE stands for Financial Independence, Retire Early. It + represents a lifestyle movement aimed at maximizing your savings + rate through increased income and/or decreased expenses to + achieve financial independence and retire much earlier than + traditional retirement age. + + + + + + How much should I save each month? + + + FIRE enthusiasts typically aim to save 50-70% of their income. + The more you can save, the faster you'll reach your FIRE + goal. However, the right amount depends on your income, + lifestyle, and target retirement age. Use the calculator to + experiment with different monthly savings amounts to see their + impact on your retirement timeline. + + + +
+ + {/* Optional: Add a section for relevant resources/links here */} +
+

+ Further Reading & Resources +

+

+ Want to learn more about FIRE and continue your journey to financial + independence? Here are some valuable resources to explore: +

+ +
+

Getting Started with FIRE:

+
    +
  1. + Read foundational content like Mr. Money Mustache's simple + math article +
  2. +
  3. + Calculate your personal numbers using this and other FIRE + calculators +
  4. +
  5. + Join communities like r/Fire to ask questions and find support +
  6. +
  7. Explore books and podcasts to deepen your understanding
  8. +
+
+ +
+ + + + + + + +
+
+
); } diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..4ca9fd0 --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,60 @@ +"use client"; + +import * as React from "react"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDown } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = "AccordionItem"; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + +)); +AccordionTrigger.displayName = "AccordionTrigger"; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); +AccordionContent.displayName = "AccordionContent"; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };