diff --git a/frontend/src/components/App.jsx b/frontend/src/components/App.jsx index 4cb08959..6b3e600c 100644 --- a/frontend/src/components/App.jsx +++ b/frontend/src/components/App.jsx @@ -32,6 +32,7 @@ export default function App() { const [searchResults, setSearchResults] = useState([]); const [duplicateCourseMessage, setDuplicateCourseMessage] = useState(''); const [selectedDetailCourse, setSelectedDetailCourse] = useState(null); + const [mobilePane, setMobilePane] = useState('schedule'); const [planEditorOptions, setPlanEditorOptions] = useState({ majorOptions: [], minorOptions: [], @@ -331,6 +332,33 @@ export default function App() { [schedule, searchResults] ); + const addSearchCourseToSemester = useCallback( + (searchResultsCourse, destSemId) => { + const currentSchedule = (schedule || DEFAULT_SCHEDULE).map((semester) => + semester.slice() + ); + if (isCourseAlreadyScheduled(currentSchedule, searchResultsCourse.id)) { + setDuplicateCourseMessage(`${searchResultsCourse.name} is already in your schedule.`); + return false; + } + + currentSchedule[destSemId].push({ + id: searchResultsCourse.id, + name: searchResultsCourse.name, + title: searchResultsCourse.title, + dist_area: searchResultsCourse.dist_area, + semester: searchResultsCourse.semester, + semester_list: searchResultsCourse.semester_list, + quality_rating: searchResultsCourse.quality_rating ?? null, + settled: [], + }); + setSchedule(currentSchedule); + setMobilePane('schedule'); + return true; + }, + [schedule] + ); + const activeThemeName = profile?.theme || DEFAULT_TIGERPATH_THEME; const activeTheme = useMemo( () => getTigerPathTheme(activeThemeName), @@ -362,17 +390,47 @@ export default function App() { .

+
+ + + +
{/* Single-page layout: search (left), schedule + plan bar (center), requirements (right). */} -
+
-
+
-
+
($active ? 600 : 500)}; position: relative; + @media (max-width: 767.98px) { + min-height: 44px; + padding: 8px 38px 8px 14px; + } + &:hover { background: ${({ $active }) => ($active ? 'var(--tp-active-plan-fill)' : 'rgba(255,255,255,0.55)')}; } @@ -80,6 +94,12 @@ const EditButton = styled.button` transform: translateY(-50%); z-index: 1; + @media (max-width: 767.98px) { + right: 8px; + min-width: 32px; + min-height: 32px; + } + &:hover { color: #111111; opacity: 1; @@ -112,6 +132,16 @@ const EditPanel = styled.div` column-gap: 24px; row-gap: 16px; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.14); + + @media (max-width: 767.98px) { + top: auto; + bottom: 12px; + transform: translateX(-50%); + width: calc(100vw - 24px); + max-height: calc(100dvh - 120px); + overflow-y: auto; + grid-template-columns: 1fr; + } `; const PanelSection = styled.div` @@ -402,6 +432,11 @@ const PanelActionButton = styled.button` transform: translateY(-1px); box-shadow: 0 5px 14px rgba(0, 0, 0, 0.09); } + + @media (max-width: 767.98px) { + justify-content: center; + gap: 10px; + } `; const Divider = styled.span` @@ -422,6 +457,11 @@ const AddButton = styled.button` font-size: 34px; line-height: 1; + @media (max-width: 767.98px) { + min-width: 44px; + min-height: 44px; + } + &:hover { color: #555555; } diff --git a/frontend/src/components/Requirements.jsx b/frontend/src/components/Requirements.jsx index af8e1c04..8790fe0d 100644 --- a/frontend/src/components/Requirements.jsx +++ b/frontend/src/components/Requirements.jsx @@ -227,6 +227,13 @@ const TabStrip = styled.div` gap: 2px; flex-shrink: 0; margin-left: 0; + + @media (max-width: 767.98px) { + overflow-x: auto; + overflow-y: hidden; + padding: 0 8px 0 0; + scrollbar-width: thin; + } `; const RidgeTab = styled.button` @@ -234,7 +241,7 @@ const RidgeTab = styled.button` border-bottom-color: ${({ $active }) => ($active ? 'white' : '#dfdfdf')}; border-radius: 18px 18px 0 0; background: ${({ $active }) => ($active ? 'white' : 'rgba(255,255,255,0.82)')}; - color: ${({ $active }) => ($active ? '#111111' : '#d3d3d3')}; + color: ${({ $active }) => ($active ? '#111111' : '#666666')}; padding: 10px 18px 9px; font-size: 20px; font-weight: ${({ $active }) => ($active ? 800 : 700)}; @@ -246,6 +253,12 @@ const RidgeTab = styled.button` z-index: 2; box-shadow: ${({ $active }) => ($active ? '0 -2px 6px rgba(0, 0, 0, 0.06)' : 'none')}; + @media (max-width: 767.98px) { + min-height: 44px; + padding: 9px 16px; + font-size: 18px; + } + &:hover { background: ${({ $active }) => ($active ? 'white' : '#fafafa')}; } @@ -291,6 +304,10 @@ const CardBody = styled.div` border-radius: 0 20px 20px 20px; overflow: hidden; box-shadow: 0 3px 12px rgba(0, 0, 0, 0.13); + + @media (max-width: 767.98px) { + border-radius: 0 14px 14px 14px; + } `; const CardContent = styled.div` @@ -298,6 +315,10 @@ const CardContent = styled.div` min-height: 0; padding: 10px 10px 14px; overflow-y: auto; + + @media (max-width: 767.98px) { + overflow-y: visible; + } `; const ProgressSummary = styled.div` @@ -315,13 +336,17 @@ const ProgressEyebrow = styled.div` font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; - color: #666666; + color: #5f5f5f; `; const YearProgressGrid = styled.div` display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; + + @media (max-width: 420px) { + grid-template-columns: 1fr; + } `; const YearProgressCard = styled.div` @@ -366,7 +391,7 @@ const YearProgressPercent = styled.div` const YearProgressLabel = styled.div` font-size: 10px; - color: #777777; + color: #666666; margin-top: 3px; `; @@ -379,7 +404,7 @@ const YearProgressName = styled.div` const YearProgressStatus = styled.div` font-size: 13px; - color: ${({ $done }) => ($done ? '#4e7d2f' : '#666666')}; + color: ${({ $done }) => ($done ? '#3a6f1f' : '#5f5f5f')}; font-weight: 700; text-align: center; `; diff --git a/frontend/src/components/Schedule.jsx b/frontend/src/components/Schedule.jsx index 817d9518..50812d09 100644 --- a/frontend/src/components/Schedule.jsx +++ b/frontend/src/components/Schedule.jsx @@ -37,6 +37,12 @@ const ScheduleCard = styled.div` box-shadow: 0 0 0 2px rgba(217, 217, 217, 0.9), 0 3px 12px rgba(0, 0, 0, 0.10); + + @media (max-width: 1279.98px) { + grid-template-columns: 1fr; + grid-template-rows: none; + overflow-y: auto; + } `; const YearBlock = styled.div` @@ -47,6 +53,17 @@ const YearBlock = styled.div` min-width: 0; ${({ $borderRight }) => $borderRight && `border-right: 2px solid rgba(217, 217, 217, 0.95);`} ${({ $borderBottom }) => $borderBottom && `border-bottom: 2px solid rgba(217, 217, 217, 0.95);`} + + @media (max-width: 1279.98px) { + border-right: 0; + border-bottom: 2px solid rgba(217, 217, 217, 0.95); + min-height: 320px; + } + + @media (max-width: 767.98px) { + padding: 14px 12px 12px; + min-height: auto; + } `; const YearTitle = styled.h2` @@ -56,6 +73,10 @@ const YearTitle = styled.h2` margin: 0 0 8px; color: #111111; letter-spacing: -0.03em; + + @media (max-width: 767.98px) { + font-size: 25px; + } `; const SemPair = styled.div` @@ -64,11 +85,20 @@ const SemPair = styled.div` min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) 2px minmax(0, 1fr); + + @media (max-width: 520px) { + grid-template-columns: 1fr; + gap: 14px; + } `; const SemColDivider = styled.div` background: ${({ theme }) => theme.lightGrey}; opacity: 0.9; + + @media (max-width: 520px) { + display: none; + } `; export default function Schedule({ onChange, profile, schedule, onCourseSelect }) { diff --git a/frontend/src/styles/CourseDetailPanel.css b/frontend/src/styles/CourseDetailPanel.css index dd1fc485..e3a97da6 100644 --- a/frontend/src/styles/CourseDetailPanel.css +++ b/frontend/src/styles/CourseDetailPanel.css @@ -1,220 +1,336 @@ +.course-detail-backdrop { + position: fixed; + inset: 0; + z-index: 190; + border: 0; + background: var(--tp-panel-overlay); + padding: 0; +} + .course-detail-panel { position: fixed; top: 0; right: 0; width: 440px; height: 100vh; - background: white; - box-shadow: -6px 0 24px rgba(0, 0, 0, 0.1); + background: var(--tp-panel-surface); + box-shadow: -6px 0 24px var(--tp-panel-shadow); z-index: 200; transform: translateX(100%); transition: transform 0.3s ease; display: flex; flex-direction: column; overflow: hidden; - border-left: 1px solid #e0e0e0; + border-left: 1px solid var(--tp-panel-border); } .course-detail-panel.open { transform: translateX(0); } -/* Close button */ .panel-collapse-btn { position: absolute; top: 16px; right: 14px; - background: none; - border: none; + min-width: 44px; + min-height: 44px; + background: transparent; + border: 0; padding: 0; - font-size: 20px; + font-size: 24px; line-height: 1; cursor: pointer; - color: #bbb; + color: var(--tp-panel-muted); z-index: 1; } + .panel-collapse-btn:hover { - color: #e74c3c; + color: var(--tp-panel-close-hover); +} + +.panel-collapse-btn:focus-visible, +.panel-error button:focus-visible, +.panel-tag:focus-visible { + outline: 3px solid color-mix(in srgb, var(--tp-accent) 45%, white); + outline-offset: 2px; } -/* Header: course title */ .panel-header { - padding: 72px 48px 14px 20px; + padding: 72px 56px 14px 20px; flex-shrink: 0; } + .panel-title { - font-size: 18px; - font-weight: 700; + font-size: 19px; + font-weight: 800; margin: 0; - color: #1a1a1a; - line-height: 1.3; + color: var(--tp-panel-text); + line-height: 1.28; } -/* Tag row */ .panel-tags { display: flex; flex-wrap: wrap; - gap: 6px; - padding: 0 20px 16px; + gap: 8px; + padding: 0 20px 18px; flex-shrink: 0; } + .panel-tag { - padding: 3px 12px; + padding: 5px 12px; border-radius: 20px; font-size: 12px; - font-weight: 600; - letter-spacing: 0.02em; + font-weight: 800; + letter-spacing: 0; + line-height: 1.2; } -.tag-code { background: #ebebeb; color: #444; } -.tag-pdf { background: #fef3cd; color: #856404; } -.tag-dist { background: #fde8e4; color: #c0392b; } + +.tag-code { + background: var(--tp-panel-tag-code-fill); + color: var(--tp-panel-tag-code-text); +} + +.tag-pdf { + background: var(--tp-panel-tag-pdf-fill); + color: var(--tp-panel-tag-pdf-text); +} + +.tag-dist { + background: var(--tp-panel-tag-dist-fill); + color: var(--tp-panel-tag-dist-text); +} + .tag-link { - background: #e8f6fa; - color: #2980b9; + background: var(--tp-panel-link-fill); + color: var(--tp-panel-link-text); text-decoration: none; - border: 1px solid #c5e8f5; + border: 1px solid var(--tp-panel-link-border); +} + +.tag-link:hover { + background: color-mix(in srgb, var(--tp-panel-link-fill) 82%, white); + color: var(--tp-accent); } -.tag-link:hover { background: #d0ecf7; color: #1a6a9a; } -/* Scrollable body */ .panel-body { flex: 1; overflow-y: auto; min-height: 0; - background: #e8eaed; - border-top: 1px solid #d8d8d8; - padding: 14px 0 4px; + background: var(--tp-panel-body); + border-top: 1px solid var(--tp-panel-border); + padding: 16px 0 10px; scrollbar-width: thin; - scrollbar-color: #ccc #e8eaed; + scrollbar-color: var(--tp-panel-scrollbar) var(--tp-panel-body); } -.panel-body::-webkit-scrollbar { width: 5px; } -.panel-body::-webkit-scrollbar-track { background: #e8eaed; } -.panel-body::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 3px; } -/* Section: label above + card below */ -.panel-section { - padding: 0 14px 12px; +.panel-body::-webkit-scrollbar { + width: 6px; } -.panel-section-comments { - flex: 1; - display: flex; - flex-direction: column; - min-height: 0; + +.panel-body::-webkit-scrollbar-track { + background: var(--tp-panel-body); +} + +.panel-body::-webkit-scrollbar-thumb { + background: var(--tp-panel-scrollbar); + border-radius: 999px; +} + +.panel-section { + padding: 0 16px 14px; } .panel-section-label { - font-size: 10.5px; - font-weight: 700; + font-size: 11px; + font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em; - color: #888; - margin-bottom: 6px; + color: var(--tp-panel-muted); + margin-bottom: 8px; padding: 0 2px; } -.panel-card { - background: white; +.panel-card, +.comment-card { + background: var(--tp-panel-surface); border-radius: 10px; - padding: 12px 14px; - box-shadow: 0 1px 3px rgba(0,0,0,0.07); + box-shadow: 0 1px 4px var(--tp-panel-card-shadow); } -/* Offerings table inside panel-card */ -.offerings-table-scroll { - max-height: 130px; - overflow-y: auto; - scrollbar-width: thin; - scrollbar-color: #ccc white; +.panel-card { + padding: 13px 15px; } -.offerings-table-scroll::-webkit-scrollbar { width: 4px; } -.offerings-table-scroll::-webkit-scrollbar-track { background: white; } -.offerings-table-scroll::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; } + .offerings-table { width: 100%; border-collapse: collapse; + table-layout: fixed; } + .offerings-table tbody tr + tr td { - border-top: 1px solid #f0f0f0; + border-top: 1px solid var(--tp-panel-rule); } + .offerings-table td { - padding: 7px 4px; + padding: 8px 4px; font-size: 13px; + vertical-align: top; } + .offerings-table td:last-child { text-align: right; white-space: nowrap; - width: 1%; + width: 64px; } -.offering-sem { font-weight: 600; min-width: 90px; white-space: nowrap; color: #444; } -.offering-professors { - color: #888; - max-width: 180px; - overflow-x: auto; + +.offering-sem { + font-weight: 800; + width: 92px; white-space: nowrap; - scrollbar-width: none; + color: var(--tp-panel-text); } -.offering-professors::-webkit-scrollbar { display: none; } + +.offering-professors { + color: var(--tp-panel-muted); + overflow: hidden; + text-overflow: ellipsis; +} + .offering-rating { - background: #e0e0e0; - padding: 2px 8px; + background: var(--tp-panel-tag-code-fill); + color: var(--tp-panel-muted); + padding: 3px 9px; border-radius: 20px; font-size: 12px; - font-weight: 600; + font-weight: 800; } -/* Description */ .panel-description-text { - font-size: 13px; - line-height: 1.6; - color: #444; - max-height: calc(1.6em * 6); - overflow-y: auto; + font-size: 14px; + line-height: 1.62; + color: var(--tp-panel-text); margin: 0; - scrollbar-width: thin; - scrollbar-color: #ccc white; } -.panel-description-text::-webkit-scrollbar { width: 4px; } -.panel-description-text::-webkit-scrollbar-track { background: white; } -.panel-description-text::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; } -/* Comments */ .comments-from { - font-weight: 400; - font-size: 10.5px; - color: #bbb; + font-weight: 600; + font-size: 11px; + color: var(--tp-panel-muted); margin-left: 6px; text-transform: none; letter-spacing: 0; } + .panel-comments { - overflow-y: auto; - flex: 1; - padding-bottom: 14px; - scrollbar-width: thin; - scrollbar-color: #ccc #e8eaed; + display: grid; + gap: 10px; } -.panel-comments::-webkit-scrollbar { width: 4px; } -.panel-comments::-webkit-scrollbar-track { background: #e8eaed; } -.panel-comments::-webkit-scrollbar-thumb { background: #c0c0c0; border-radius: 3px; } + .comment-card { - background: white; - border-radius: 8px; - padding: 10px 14px; - margin-bottom: 8px; - font-size: 13px; + padding: 12px 14px; + font-size: 14px; line-height: 1.55; - color: #444; - box-shadow: 0 1px 3px rgba(0,0,0,0.07); + color: var(--tp-panel-text); } -.panel-loading { +.panel-loading, +.panel-no-data, +.panel-error { + margin: 0; padding: 20px; - color: #aaa; - font-size: 13px; + color: var(--tp-panel-muted); + font-size: 14px; } + .panel-no-data { - padding: 20px; - color: #bbb; - font-size: 13px; font-style: italic; } + +.panel-error p { + margin: 0 0 10px; +} + +.panel-error button { + min-height: 40px; + border: 1px solid var(--tp-search-action-border); + border-radius: 999px; + background: var(--tp-search-action-fill); + color: var(--tp-search-action-text); + padding: 0 16px; + font-weight: 800; +} + +@media (max-width: 767.98px) { + .course-detail-backdrop { + background: var(--tp-panel-overlay-mobile); + } + + .course-detail-panel { + top: auto; + right: 0; + bottom: 0; + left: 0; + width: 100vw; + height: min(86dvh, 720px); + border-left: none; + border-radius: 18px 18px 0 0; + transform: translateY(100%); + } + + .course-detail-panel.open { + transform: translateY(0); + } + + .panel-collapse-btn { + top: 12px; + right: 12px; + } + + .panel-header { + padding: 64px 64px 14px 18px; + } + + .panel-title { + font-size: 21px; + } + + .panel-tags { + padding: 0 18px 16px; + } + + .panel-body { + padding: 16px 0 18px; + } + + .panel-section { + padding: 0 14px 16px; + } + + .panel-card { + padding: 14px; + } + + .offerings-table { + table-layout: auto; + } + + .offerings-table tr { + display: grid; + grid-template-columns: minmax(86px, auto) 1fr auto; + column-gap: 10px; + align-items: start; + } + + .offerings-table td { + display: block; + padding: 9px 0; + } + + .offering-sem { + width: auto; + } + + .offering-professors { + min-width: 0; + } +} diff --git a/frontend/src/styles/Courses.css b/frontend/src/styles/Courses.css index c49f6ab9..364827a7 100644 --- a/frontend/src/styles/Courses.css +++ b/frontend/src/styles/Courses.css @@ -13,7 +13,7 @@ .search-input-icon { position: absolute; left: 18px; - color: #b8b8b8; + color: #6f6f6f; font-size: 18px; z-index: 1; } @@ -30,13 +30,13 @@ } #search-courses .form-control::placeholder { - color: #b0b0b0; + color: #6f6f6f; } #search-courses .form-control:focus { - border-color: #d7d7d7; - box-shadow: none; - outline: 0 none; + border-color: var(--tp-accent); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--tp-accent) 18%, transparent); + outline: 0; background: #ffffff; } @@ -70,7 +70,7 @@ .search-card { font-size: 12px; height: auto; - padding: 10px; + padding: 9px; margin: 0 0 10px; border-radius: 8px; background: linear-gradient(180deg, var(--tp-search-fill-start) 0%, var(--tp-search-fill-end) 100%); @@ -78,6 +78,10 @@ border: none; box-shadow: none; user-select: none; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 8px; + align-items: start; } .search-card.fall, @@ -87,40 +91,116 @@ color: var(--tp-search-text); } -.search-card-info { +.search-card-main { + min-width: 0; + border: 0; + background: transparent; + color: inherit; + padding: 0; + text-align: left; display: grid; - grid-template-columns: 1fr auto; - grid-gap: 4px; - align-items: center; - font-size: 12px; - line-height: 1.2; - margin-top: 8px; + gap: 8px; + cursor: inherit; } -.search-card-info-actions { - display: inline-flex; - align-items: center; - gap: 8px; +.search-card-main:focus-visible, +.search-card-info-btn:focus-visible, +.search-card-add-btn:focus-visible, +.search-card-add-menu select:focus-visible, +.search-card-add-menu button:focus-visible { + outline: 3px solid color-mix(in srgb, var(--tp-accent) 45%, white); + outline-offset: 2px; } -.search-card-info .course-title { +.search-card-main .course-title { color: var(--tp-search-text); - font-weight: 500; + font-size: 12px; + font-weight: 600; + line-height: 1.25; + overflow-wrap: anywhere; } .search-card-info-btn { - color: inherit; - opacity: 0.45; + align-self: start; + color: var(--tp-search-text); + opacity: 0.62; transition: color 0.15s ease, opacity 0.15s ease; + min-width: 36px; + min-height: 36px; + border: 0; + background: transparent; } .search-card-info-btn:hover, -.search-card-info-btn:focus, +.search-card-info-btn:focus-visible, .search-card-info-btn:active { color: var(--tp-accent); opacity: 1; } +.search-card-add-btn { + min-height: 36px; + align-self: start; + border: 1px solid var(--tp-search-action-border); + border-radius: 999px; + background: color-mix(in srgb, var(--tp-search-action-fill) 84%, transparent); + color: var(--tp-search-action-text); + display: inline-flex; + align-items: center; + gap: 5px; + padding: 0 10px; + font-size: 13px; + font-weight: 800; + line-height: 1; +} + +.search-card-add-btn:hover, +.search-card-add-btn:focus-visible, +.search-card-add-btn[aria-expanded='true'] { + border-color: var(--tp-accent); + background: var(--tp-search-action-fill); + color: var(--tp-accent); +} + +.search-card-add-menu { + display: grid; + grid-column: 1 / -1; + grid-template-columns: minmax(0, 1fr) auto; + gap: 8px; + align-items: center; + margin-top: 2px; + padding-top: 9px; + border-top: 1px solid color-mix(in srgb, var(--tp-search-text) 16%, transparent); +} + +.search-card-add-menu select, +.search-card-add-menu button { + min-height: 40px; + border: 1px solid var(--tp-search-action-border); + border-radius: 8px; + background: var(--tp-search-action-fill); + color: var(--tp-search-action-text); + font-size: 13px; + font-weight: 800; +} + +.search-card-add-menu select { + width: 100%; + min-width: 0; + padding: 0 10px; +} + +.search-card-add-menu button { + padding: 0 12px; + white-space: nowrap; +} + +.search-card-add-menu button:hover, +.search-card-add-menu button:focus-visible { + border-color: var(--tp-accent); + color: var(--tp-accent); +} + .search-card .search-card-links { visibility: visible; } @@ -190,7 +270,7 @@ } .course-popover-info-link { - color: #0d6efd; + color: #075fc7; display: inline-flex; align-items: center; justify-content: center; @@ -282,7 +362,7 @@ .search-card .search-course-pill { background: var(--tp-search-pill-fill); - border: 1px solid rgba(158, 112, 214, 0.18); + border: 1px solid color-mix(in srgb, var(--tp-search-action-border) 42%, transparent); width: 100%; box-sizing: border-box; padding: 0 16px; diff --git a/frontend/src/styles/Requirements.css b/frontend/src/styles/Requirements.css index 52cf04d2..0f32dfd5 100644 --- a/frontend/src/styles/Requirements.css +++ b/frontend/src/styles/Requirements.css @@ -92,6 +92,8 @@ display: inline-flex; align-items: center; justify-content: center; + min-width: 32px; + min-height: 32px; } #requirements .external-credit-remove:hover, @@ -241,7 +243,7 @@ } #requirements .reqCount-incomplete .reqCountCurrent { - color: #ef5a47; + color: #c53d2f; } #requirements { @@ -253,6 +255,23 @@ gap: 16px; } +@media (max-width: 767.98px) { + #requirements { + overflow: visible; + } + + #requirements .req-course-chip, + #requirements li { + min-height: 44px; + font-size: 16px; + } + + #requirements .external-credit-remove { + min-width: 44px; + min-height: 44px; + } +} + .fa-search { padding-right: 5px; } @@ -263,12 +282,13 @@ align-items: center; justify-content: center; font-weight: 800; + min-height: 44px; } .searchByReq { background: #eef5ff; border: 1px solid #7da0ce; - color: #21476f; + color: #163f68; } .searchByReq:hover, @@ -372,7 +392,7 @@ font-weight: 800; font-size: 16px; margin-bottom: 10px; - color: #000; + color: #111111; line-height: 1.1; } @@ -387,7 +407,7 @@ .contact-role { font-weight: 700; font-size: 14px; - color: #000; + color: #111111; display: block; line-height: 1.15; } @@ -395,7 +415,7 @@ .contact-name { font-weight: 800; font-size: 15px; - color: #000; + color: #111111; line-height: 1.15; margin-top: 2px; margin-bottom: 2px; @@ -414,14 +434,14 @@ display: block; font-weight: 700; font-size: 15px; - color: #000; + color: #111111; text-decoration: none; margin-bottom: 8px; line-height: 1.2; } a.ref-link-item { - color: #0d6efd; + color: #075fc7; text-decoration: underline; } diff --git a/frontend/src/styles/app-style.css b/frontend/src/styles/app-style.css index 0c670f1f..86e3033e 100644 --- a/frontend/src/styles/app-style.css +++ b/frontend/src/styles/app-style.css @@ -14,11 +14,36 @@ body { --tp-search-fill-end: #ded3ff; --tp-search-pill-fill: rgba(188, 137, 237, 0.45); --tp-search-text: #5922a5; + --tp-search-action-fill: #ffffff; + --tp-search-action-text: #3f167a; + --tp-search-action-border: #b89de8; --tp-active-plan-fill: #ddd6ff; --tp-active-plan-border: #b99df7; --tp-requirement-fill-start: #dff6b4; --tp-requirement-fill-end: #e1ff9a; - --tp-requirement-text: #0da745; + --tp-requirement-text: #247427; + --tp-panel-surface: #ffffff; + --tp-panel-body: #f0f1f3; + --tp-panel-border: #d8d8d8; + --tp-panel-rule: #eeeeee; + --tp-panel-overlay: rgba(18, 18, 18, 0.18); + --tp-panel-overlay-mobile: rgba(18, 18, 18, 0.28); + --tp-panel-shadow: rgba(0, 0, 0, 0.12); + --tp-panel-card-shadow: rgba(0, 0, 0, 0.08); + --tp-panel-scrollbar: #bfc2c8; + --tp-panel-muted: #5f5f5f; + --tp-panel-text: #242424; + --tp-panel-close-hover: #b3261e; + --tp-panel-rating-text: #ffffff; + --tp-panel-tag-code-fill: #ebebeb; + --tp-panel-tag-code-text: #3f3f3f; + --tp-panel-tag-pdf-fill: #fff2c5; + --tp-panel-tag-pdf-text: #6c5100; + --tp-panel-tag-dist-fill: #efe0ff; + --tp-panel-tag-dist-text: #4a1f87; + --tp-panel-link-fill: #eee7ff; + --tp-panel-link-text: #3f167a; + --tp-panel-link-border: #cbbaf4; --tp-accent: #4a1f87; } h1, @@ -106,6 +131,10 @@ h6 { align-items: stretch; } +#mobile-pane-tabs { + display: none; +} + #app > [class*='col-'] { width: auto; max-width: none; @@ -151,6 +180,18 @@ h6 { display: none; } +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + /* scrollbars — narrow, blending with cream background */ ::-webkit-scrollbar { width: 8px; @@ -283,10 +324,64 @@ div#banner-content { text-align: center; } -/* Show banner on medium devices (less than 992px) */ +@media (min-width: 992px) and (max-width: 1279.98px) { + #app { + grid-template-columns: minmax(560px, 1fr) minmax(280px, 0.46fr); + grid-template-rows: auto minmax(0, 1fr); + grid-template-areas: + "main search" + "main requirements"; + height: calc(100vh - 78px); + } + + #search-pane { + grid-area: search; + border-right: none; + border-left: 1px solid #e6e6e6; + padding-left: 14px; + } + + #main-view-pane { + grid-area: main; + } + + #app .requirements-pane { + grid-area: requirements; + padding-top: 8px; + padding-left: 14px; + border-left: 1px solid #e6e6e6; + } +} + @media (max-width: 991.98px) { + body { + overflow: auto; + } + div#banner { - display: block; + display: none; + } + + .navbar { + min-height: auto; + padding-top: 14px; + padding-bottom: 14px; + } + + .navbar .container { + align-items: flex-start; + gap: 12px; + padding-left: 18px; + padding-right: 18px; + } + + .navbar-brand { + font-size: 28px; + } + + .nav-btn { + min-height: 44px; + padding: 8px 10px; } #app { @@ -314,6 +409,65 @@ div#banner-content { } } +@media (max-width: 767.98px) { + #mobile-pane-tabs { + position: sticky; + top: 0; + z-index: 30; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + padding: 10px 12px 6px; + background: #ffffff; + border-bottom: 1px solid #e8e8e8; + } + + #mobile-pane-tabs button { + border: 1px solid #d8d8d8; + border-radius: 999px; + min-height: 44px; + background: #ffffff; + color: #171717; + font-weight: 800; + } + + #mobile-pane-tabs button.active { + background: var(--tp-active-plan-fill); + border-color: var(--tp-active-plan-border); + color: var(--tp-accent); + } + + #app { + display: block; + min-height: calc(100dvh - 150px); + padding: 10px 12px 20px; + } + + #app .mobile-pane { + display: none; + } + + #app .mobile-pane.mobile-pane-active { + display: flex; + } + + #search-pane, + #main-view-pane, + #app .requirements-pane { + width: 100%; + min-height: calc(100dvh - 176px); + padding: 8px 0 0; + border: none; + } + + #display-courses { + height: auto; + max-height: none; + overflow: visible; + padding-right: 0; + } +} + @media print { body { overflow: visible; diff --git a/frontend/src/styles/theme.js b/frontend/src/styles/theme.js index c566086b..165b230f 100644 --- a/frontend/src/styles/theme.js +++ b/frontend/src/styles/theme.js @@ -21,11 +21,19 @@ export const TIGERPATH_THEME_OPTIONS = { searchFillEnd: '#ded3ff', searchPillFill: 'rgba(188, 137, 237, 0.45)', searchText: '#5922a5', + searchActionFill: '#ffffff', + searchActionText: '#3f167a', + searchActionBorder: '#b89de8', activePlanFill: '#ddd6ff', activePlanBorder: '#b99df7', requirementFillStart: '#dff6b4', requirementFillEnd: '#e1ff9a', - requirementText: '#0da745', + requirementText: '#247427', + panelTagDistFill: '#efe0ff', + panelTagDistText: '#4a1f87', + panelLinkFill: '#eee7ff', + panelLinkText: '#3f167a', + panelLinkBorder: '#cbbaf4', accent: '#4a1f87', }, orange: { @@ -34,18 +42,26 @@ export const TIGERPATH_THEME_OPTIONS = { fallSemHeaderColor: '#ffad7a', springSemHeaderColor: '#ffe7d6', ECHeaderColor: '#ef6c45', - courseText: '#ef3f22', + courseText: '#a83312', courseFill: '#ffe6da', courseBorder: '#ffc9b0', searchFillStart: '#ffd1bd', searchFillEnd: '#ffe0d2', searchPillFill: 'rgba(255, 151, 104, 0.36)', - searchText: '#d9471c', + searchText: '#a83312', + searchActionFill: '#fffaf6', + searchActionText: '#7e290d', + searchActionBorder: '#ffad7a', activePlanFill: '#ffe6da', activePlanBorder: '#ffad7a', requirementFillStart: '#eaffb8', requirementFillEnd: '#e1ff9a', - requirementText: '#289b2f', + requirementText: '#3a6f1f', + panelTagDistFill: '#ffe6da', + panelTagDistText: '#8a2a0d', + panelLinkFill: '#fff1e8', + panelLinkText: '#7e290d', + panelLinkBorder: '#ffbd96', accent: '#c64019', }, blue: { @@ -61,11 +77,19 @@ export const TIGERPATH_THEME_OPTIONS = { searchFillEnd: '#dff6fb', searchPillFill: 'rgba(116, 205, 218, 0.34)', searchText: '#087789', + searchActionFill: '#ffffff', + searchActionText: '#075f6e', + searchActionBorder: '#80d4de', activePlanFill: '#dcf4f6', activePlanBorder: '#80d4de', requirementFillStart: '#eaffb8', requirementFillEnd: '#e1ff9a', - requirementText: '#17a244', + requirementText: '#247427', + panelTagDistFill: '#ddf5f9', + panelTagDistText: '#075f6e', + panelLinkFill: '#e7f8fb', + panelLinkText: '#075f6e', + panelLinkBorder: '#9adce5', accent: '#006f80', }, pink: { @@ -74,18 +98,26 @@ export const TIGERPATH_THEME_OPTIONS = { fallSemHeaderColor: '#ff9fc7', springSemHeaderColor: '#ffe4ef', ECHeaderColor: '#f46196', - courseText: '#f02372', + courseText: '#a80e4c', courseFill: '#ffe3ef', courseBorder: '#ffc1da', searchFillStart: '#ffd2e5', searchFillEnd: '#ffe2ef', searchPillFill: 'rgba(255, 135, 186, 0.35)', - searchText: '#d71b68', + searchText: '#a80e4c', + searchActionFill: '#ffffff', + searchActionText: '#83093b', + searchActionBorder: '#ff9fc7', activePlanFill: '#ffe3ef', activePlanBorder: '#ff9fc7', requirementFillStart: '#eaffb8', requirementFillEnd: '#e1ff9a', - requirementText: '#17a340', + requirementText: '#247427', + panelTagDistFill: '#ffe3ef', + panelTagDistText: '#83093b', + panelLinkFill: '#fff0f6', + panelLinkText: '#83093b', + panelLinkBorder: '#ffb3d2', accent: '#b70f55', }, }; @@ -107,11 +139,36 @@ export function getTigerPathThemeVariables(themeName) { '--tp-search-fill-end': theme.searchFillEnd, '--tp-search-pill-fill': theme.searchPillFill, '--tp-search-text': theme.searchText, + '--tp-search-action-fill': theme.searchActionFill, + '--tp-search-action-text': theme.searchActionText, + '--tp-search-action-border': theme.searchActionBorder, '--tp-active-plan-fill': theme.activePlanFill, '--tp-active-plan-border': theme.activePlanBorder, '--tp-requirement-fill-start': theme.requirementFillStart, '--tp-requirement-fill-end': theme.requirementFillEnd, '--tp-requirement-text': theme.requirementText, + '--tp-panel-surface': '#ffffff', + '--tp-panel-body': '#f0f1f3', + '--tp-panel-border': '#d8d8d8', + '--tp-panel-rule': '#eeeeee', + '--tp-panel-overlay': 'rgba(18, 18, 18, 0.18)', + '--tp-panel-overlay-mobile': 'rgba(18, 18, 18, 0.28)', + '--tp-panel-shadow': 'rgba(0, 0, 0, 0.12)', + '--tp-panel-card-shadow': 'rgba(0, 0, 0, 0.08)', + '--tp-panel-scrollbar': '#bfc2c8', + '--tp-panel-muted': '#5f5f5f', + '--tp-panel-text': '#242424', + '--tp-panel-close-hover': '#b3261e', + '--tp-panel-rating-text': '#ffffff', + '--tp-panel-tag-code-fill': '#ebebeb', + '--tp-panel-tag-code-text': '#3f3f3f', + '--tp-panel-tag-pdf-fill': '#fff2c5', + '--tp-panel-tag-pdf-text': '#6c5100', + '--tp-panel-tag-dist-fill': theme.panelTagDistFill, + '--tp-panel-tag-dist-text': theme.panelTagDistText, + '--tp-panel-link-fill': theme.panelLinkFill, + '--tp-panel-link-text': theme.panelLinkText, + '--tp-panel-link-border': theme.panelLinkBorder, '--tp-accent': theme.accent, }; }