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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
# The tests are .mts files run by Node's built-in test runner, which
# needs a Node version that strips TypeScript types natively.
node-version: 24
cache: npm
- run: npm ci
# PageProps/LayoutProps are generated into .next/types, which a fresh
# checkout doesn't have, so tsc can't see them without this first.
- name: Generate route types
run: npx next typegen
- name: Typecheck
run: npx tsc --noEmit
- name: Lint
run: npm run lint
- name: Test
run: npm test
- name: Build
run: npm run build
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Engineering Exploration

[![CI](https://github.com/jgomezrom/engineering-exploration/actions/workflows/ci.yml/badge.svg)](https://github.com/jgomezrom/engineering-exploration/actions/workflows/ci.yml)

A website to help students figure out which engineering field might actually fit them, built as a personal project by **Jaime Gomez**, a student at MSU.

**Live site:** https://engineering-exploration-two.vercel.app
Expand All @@ -17,6 +19,9 @@ Back in high school I remember looking at a long list of engineering majors and
- **A 28-question interest quiz** scoring a percentage against all 23 fields, with a radar chart, plus a shorter bonus quiz for the less familiar majors
- **12 hands-on challenges** with real materials lists, costs, time estimates and step-by-step instructions
- **Fully bilingual (English/Spanish)** — every page, every simulation, every field
- **A real photograph for every field** — public-domain and CC0 images from NASA, the Library of Congress (the Historic American Engineering Record and the Carol M. Highsmith Archive), the U.S. Navy and Wikimedia Commons, each credited and deep-linked to its source record so a caption can be re-checked
- **Optional school color themes** — pick a Big Ten or SEC school and the whole site re-themes to its colors
- **Built to be shared and found** — every page has its own share preview with a generated image, a canonical URL, and a place in the sitemap, plus structured data so search engines know what the site is
- 64 pages, all statically generated
- Custom illustrations, icons and charts are hand-built SVG — no stock photos, no icon library, no charting library
- Accessible: real focus management on dialogs, keyboard traps where modals need them, checked contrast and heading structure, and `prefers-reduced-motion` respected
Expand Down Expand Up @@ -44,13 +49,16 @@ npm test

22 tests covering the three solvers, using Node's built-in test runner — **no test framework added as a dependency**. They check the beam against `Pab/L` and `PL³/48EI`, the pendulum against published elliptic-integral period ratios, and the projectile's drag solution against a second integrator written independently inside the test, since agreeing with a different method is a real check while agreeing with itself is not. They also check things that must be true regardless: equilibrium holds, moment returns to zero at both supports, amplitude doesn't decay in a frictionless swing, and air never adds range.

GitHub Actions runs the typecheck, lint, tests and a full production build on every push and pull request, so a broken change shows up before it reaches the live site.

## Tech stack

- **Next.js 16** (App Router), **React 19**, **TypeScript**
- **Tailwind CSS v4** for styling
- No backend, no database — it's a fully static site, generated at build time
- No UI kit, no charting library, no physics library, no test framework — the solvers, the radar chart and every illustration are hand-written
- `app/lib/` holds the numerical solvers as plain functions, separate from the UI, which is what makes them testable
- **GitHub Actions** for CI, **Vercel** for hosting — every merge to `main` deploys automatically

## How this was actually built

Expand All @@ -66,6 +74,7 @@ I didn't type most of these lines of code. But I know why every part of this sit
- Why sourcing matters: I had this project verify salary numbers against the actual BLS pages instead of trusting a search summary, and it caught an earlier mistake where a sitemap pointed at the wrong domain because a similarly-named site already existed
- That it's easy to accidentally write "advertising" language even when you're trying to be honest, and worth specifically reviewing your own content for it
- The basics of deploying to Vercel, environment variables, and why a sitemap needs to know its own real production URL to be useful at all
- That a framework can quietly undo your work: in Next.js, a page that sets its own share-preview (Open Graph) title replaces the parent layout's share settings entirely instead of merging with them, so adding per-page titles silently dropped the preview image from every page but the homepage — caught by checking the actual `<head>` tags, not by assuming it worked
- That a numerical answer nobody has checked isn't really an answer — every solver here gets compared against an exact solution wherever one exists, and the error goes on the page instead of staying in my head

## Running it locally
Expand Down
4 changes: 3 additions & 1 deletion app/components/CollegeContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { fields } from "../data/fields";
import { fieldsEs } from "../data/fields.es";
import { collegeCurricula } from "../data/collegeCurricula";
import { collegeTips } from "../data/collegeTips";
import { collegeTipsEs } from "../data/collegeTips.es";
import { CurriculumSequence, CurriculumYear, FieldSlug, TipTheme } from "../data/types";
import { useLanguage } from "../context/LanguageContext";
import { collegeTranslations, tipThemeLabels, tipConfidenceLabels, collegeOnlyMajorNames } from "../data/translations/college";
Expand Down Expand Up @@ -57,6 +58,7 @@ export default function CollegeContent() {
const themeLabel = tipThemeLabels[language];
const confidenceLabel = tipConfidenceLabels[language];
const displayFields = language === "es" ? fieldsEs : fields;
const displayTips = language === "es" ? collegeTipsEs : collegeTips;
const majorNameFallback = collegeOnlyMajorNames[language];
const majorName = useCallback(
(slug: FieldSlug) => displayFields.find((f) => f.slug === slug)?.name ?? majorNameFallback[slug] ?? slug,
Expand Down Expand Up @@ -167,7 +169,7 @@ export default function CollegeContent() {

<div className="mt-8 space-y-10">
{THEME_ORDER.map((theme) => {
const themeTips = collegeTips.filter((tip) => tip.theme === theme);
const themeTips = displayTips.filter((tip) => tip.theme === theme);
if (themeTips.length === 0) return null;
return (
<div key={theme}>
Expand Down
60 changes: 60 additions & 0 deletions app/data/collegeTips.es.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CollegeTip } from "./types";

// Spanish translation of collegeTips.ts. `slug`, `theme`, `confidence`, and
// `sourceUrl` stay identical to the English entries — they're keys and links,
// not display text. Keep each claim exactly as strong as its source.
export const collegeTipsEs: CollegeTip[] = [
{
slug: "active-problem-solving-vs-passive-review",
text: "Enfócate en resolver problemas activamente y en discusiones entre compañeros, en lugar de releer pasivamente las diapositivas de clase. En un estudio aleatorizado de 2019 con cursos introductorios de física universitaria, los estudiantes a quienes se les enseñó con métodos activos aprendieron más que los que tuvieron clases magistrales con profesores muy bien evaluados — pero sintieron que habían aprendido menos, porque interpretaron el esfuerzo mental adicional como señal de que no estaban entendiendo. Sentir que te cuesta mientras resuelves problemas no significa que no esté funcionando.",
theme: "study-strategies",
confidence: "research-backed",
dependsOn:
"La disposición del estudiante a aceptar la incomodidad de resolver problemas de práctica difíciles desde cero, en lugar de tomar el camino \"fácil\" de solo ver a un profesor resolverlos en el pizarrón.",
sourceUrl: "https://pmc.ncbi.nlm.nih.gov/articles/PMC6765278/",
},
{
slug: "secure-an-internship-before-graduation",
text: "Dale prioridad a conseguir al menos una pasantía o prácticas profesionales en la industria (o un programa co-op) antes de graduarte. La investigación en educación cuenta las pasantías entre las \"prácticas de alto impacto\" asociadas con mayor participación y permanencia de los estudiantes, y en un estudio de 2018, los pasantes de una empresa de ingeniería pudieron nombrar habilidades específicas que aprendieron en el trabajo.",
theme: "internships",
confidence: "research-backed",
dependsOn:
"La meta profesional final del estudiante. Si un estudiante tiene la intención firme de hacer un doctorado y dedicarse a la academia, participar en investigación de laboratorio durante la licenciatura generalmente tendrá prioridad sobre las pasantías en la industria.",
sourceUrl:
"https://par.nsf.gov/biblio/10076373-exploring-how-engineering-internships-undergraduate-research-experiences-inform-influence-college-students-career-decisions-future-plans",
},
{
slug: "front-load-hard-prerequisite-chains",
text: "Toma pronto las cadenas largas de prerrequisitos, y planea para tu segundo o tercer año las materias que tienen fama de ser más difíciles.",
theme: "course-planning",
confidence: "personal experience",
dependsOn:
"El mapa curricular específico de tu universidad y la disponibilidad de clases. Esta estrategia solo funciona si el departamento ofrece esas materias clave en los semestres en que las necesitas, y requiere una asesoría académica cuidadosa para no sobrecargar un solo semestre.",
},
{
slug: "join-a-research-lab-sophomore-year",
text: "Ofrécete como voluntario en el laboratorio de investigación de un profesor a más tardar en tu segundo año, para ganar habilidades técnicas prácticas y experiencia con equipo que las materias básicas normales no te van a enseñar.",
theme: "research",
confidence: "varies by situation",
dependsOn:
"La disponibilidad del profesorado, el financiamiento del departamento y el tipo de institución. Es muy factible en universidades grandes de investigación (las llamadas universidades R1, según la clasificación de EE. UU.) pero puede ser mucho más difícil de conseguir en universidades más pequeñas enfocadas en la enseñanza, con poco espacio de laboratorio para posgrado.",
},
{
slug: "input-focused-study-goals",
text: 'Ponte metas de estudio "enfocadas en el proceso" (por ejemplo, "voy a hacer problemas de práctica concentrado durante dos horas") en lugar de metas "enfocadas en el resultado" (por ejemplo, "hoy voy a memorizar todo este capítulo").',
theme: "study-strategies",
confidence: "varies by situation",
dependsOn:
"La autodisciplina básica del estudiante. Las metas enfocadas en el proceso solo funcionan si el estudiante se hace responsable de trabajar de verdad, concentrado y sin distracciones, durante esos bloques de tiempo, en lugar de solo dejar correr el reloj.",
sourceUrl: "https://www.youtube.com/watch?v=5JH130NIR8k",
},
{
slug: "schedule-regular-exercise",
text: "Pon el ejercicio físico en tu calendario semanal como tiempo protegido, no como algo que harás cuando termines el trabajo — con una carga académica pesada, el trabajo casi nunca se termina. Una revisión sistemática de 2022, con 18 estudios que abarcaron a 11,500 estudiantes de medicina en 13 países, encontró que los estudiantes más activos físicamente tenían menos agotamiento (burnout) y mejor calidad de vida, y que más actividad tendía a ir de la mano con diferencias mayores. Es una asociación, no una prueba de que el ejercicio prevenga el burnout.",
theme: "workload-and-burnout",
confidence: "research-backed",
dependsOn:
"Tu horario y cómo lo administras. El ejercicio solo ayuda si se acomoda alrededor de tus fechas de entrega importantes, en lugar de quitarte tiempo de estudio que no te puedes permitir perder — y la investigación detrás de este consejo viene de estudiantes de medicina, no específicamente de estudiantes de ingeniería.",
sourceUrl: "https://pmc.ncbi.nlm.nih.gov/articles/PMC9826463/",
},
];
16 changes: 8 additions & 8 deletions app/data/collegeTips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { CollegeTip } from "./types";
export const collegeTips: CollegeTip[] = [
{
slug: "active-problem-solving-vs-passive-review",
text: "Focus on active problem-solving and peer-led discussions rather than passively re-reading lecture slides. Students who engage in active learning develop better higher-order cognitive skills and retain information longer, even if the increased cognitive effort makes them feel like they are struggling more in the moment.",
text: "Focus on active problem-solving and peer-led discussions rather than passively re-reading lecture slides. In a 2019 randomized study of introductory college physics courses, students taught with active methods learned more than students taught by highly rated lecturers — yet felt like they had learned less, because they took the extra mental effort as a sign they weren't getting it. Feeling like you're struggling while you work problems doesn't mean it isn't working.",
theme: "study-strategies",
confidence: "research-backed",
dependsOn:
"The student's willingness to embrace the discomfort of working through difficult practice problems from scratch rather than taking the \"easy\" route of just watching a professor solve them on a board.",
sourceUrl: "https://adiutor.co/blog/impact-of-active-versus-passive-learning-approaches-on-student-academic-performance/",
sourceUrl: "https://pmc.ncbi.nlm.nih.gov/articles/PMC6765278/",
},
{
slug: "secure-an-internship-before-graduation",
text: "Prioritize securing at least one industry internship or co-op before graduation. Hands-on experience fundamentally alters career trajectories, clarifies career choices, and significantly builds professional competencies compared to classroom theory alone.",
text: "Prioritize securing at least one industry internship or co-op before graduation. Education research counts internships among the \"high-impact practices\" linked to higher student engagement and retention, and in a 2018 study, interns at an engineering firm could name specific skills they had picked up on the job.",
theme: "internships",
confidence: "research-backed",
dependsOn:
Expand All @@ -34,7 +34,7 @@ export const collegeTips: CollegeTip[] = [
theme: "research",
confidence: "varies by situation",
dependsOn:
"Faculty availability, department funding, and the type of institution. This is highly feasible at large research institutions (R1 universities) but may be much harder to secure at smaller teaching-focused colleges with limited graduate lab space.",
"Faculty availability, department funding, and the type of institution. This is highly feasible at large research institutions (R1 universities, in the U.S. classification) but may be much harder to secure at smaller teaching-focused colleges with limited graduate lab space.",
},
{
slug: "input-focused-study-goals",
Expand All @@ -46,12 +46,12 @@ export const collegeTips: CollegeTip[] = [
sourceUrl: "https://www.youtube.com/watch?v=5JH130NIR8k",
},
{
slug: "schedule-deliberate-breaks-and-exercise",
text: 'Schedule deliberate "waste-of-time" activities and physical exercise into your weekly calendar to combat the physical exhaustion and cognitive impairment that comes with intense coursework. The unrelenting quest for perfection in engineering makes stepping away entirely critical to preventing emotional detachment.',
slug: "schedule-regular-exercise",
text: "Put physical exercise on your weekly calendar as protected time, not something you'll get to once the work is done — in a heavy course load, the work is rarely done. A 2022 systematic review of 18 studies covering 11,500 medical students in 13 countries found that students who were more physically active had lower burnout and a better quality of life, with more activity tending to go with bigger differences. That's an association, not proof that exercise prevents burnout.",
theme: "workload-and-burnout",
confidence: "research-backed",
dependsOn:
"The individual's time-management skills. Taking deliberate breaks is an effective recovery tool only if the student is consistently utilizing their study blocks productively and not falling behind on critical deadlines.",
sourceUrl: "https://www.sandipuniversity.edu.in/blog/signs-of-burnout-in-engineering-students/",
"Your schedule and how you manage it. Exercise only helps if it fits around your critical deadlines instead of eating study time you can't spare — and the research behind this tip comes from medical students, not engineering students specifically.",
sourceUrl: "https://pmc.ncbi.nlm.nih.gov/articles/PMC9826463/",
},
];
4 changes: 3 additions & 1 deletion app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ImageResponse } from "next/og";
import { fields } from "./data/fields";

export const size = { width: 1200, height: 630 };
export const contentType = "image/png";
Expand Down Expand Up @@ -80,7 +81,8 @@ export default function Image() {
</div>

<div style={{ display: "flex", fontSize: 30, color: "#52525b", marginTop: 24, textAlign: "center", maxWidth: 880 }}>
Real engineering, no hype — 23 fields, honest tradeoffs, hands-on challenges
{/* One string, not inline {fields.length}: Satori lays out each JSX text fragment as its own flex item. */}
{`Real engineering, no hype — ${fields.length} fields, honest tradeoffs, hands-on challenges`}
</div>
</div>
),
Expand Down
10 changes: 8 additions & 2 deletions app/quiz/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import type { Metadata } from "next";
import { fields } from "../data/fields";
import { quizQuestions } from "../data/quiz";

const title = "Which Engineering Field Fits You? | Engineering Exploration";
const description = `Take a free ${quizQuestions.length}-question interest quiz and get a percentage match across ${fields.length} engineering fields, based on how you actually answered — not a verdict, a starting point.`;

export const metadata: Metadata = {
title: "Which Engineering Field Fits You? | Engineering Exploration",
description: `Take a free ${quizQuestions.length}-question interest quiz and get a percentage match across ${fields.length} engineering fields, based on how you actually answered — not a verdict, a starting point.`,
title,
description,
alternates: { canonical: "/quiz" },
openGraph: { title, description, url: "/quiz", images: "/opengraph-image" },
twitter: { title, description, images: "/opengraph-image" },
};

export default function QuizLayout({ children }: { children: React.ReactNode }) {
Expand Down
12 changes: 9 additions & 3 deletions app/quiz/more-majors/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type { Metadata } from "next";

const title = "The Smaller Majors Quiz | Engineering Exploration";
const description =
"An optional bonus round for seven easy-to-overlook fields — architectural, automotive, energy, manufacturing, semiconductor, structural, and systems engineering. A tally of interest, not a percentage match.";

export const metadata: Metadata = {
title: "The Smaller Majors Quiz | Engineering Exploration",
description:
"An optional, more informal quiz for fields like nuclear, petroleum, marine, and semiconductor engineering — a tally of interest, not a percentage match.",
title,
description,
alternates: { canonical: "/quiz/more-majors" },
openGraph: { title, description, url: "/quiz/more-majors", images: "/opengraph-image" },
twitter: { title, description, images: "/opengraph-image" },
};

export default function MoreMajorsQuizLayout({ children }: { children: React.ReactNode }) {
Expand Down
Loading
Loading