From 99a9e954f706fc17434b172d249968b6b8723ac5 Mon Sep 17 00:00:00 2001 From: "ALMAZROUEI Shamma (2021) WKIS203" <shamma.almazrouei.2021@live.rhul.ac.uk> Date: Wed, 9 Apr 2025 19:21:58 +0530 Subject: [PATCH] fix accessibility options --- uniconnect-app/app/app/layout.tsx | 6 +- uniconnect-app/app/globals.css | 90 ++++++----- uniconnect-app/app/layout.tsx | 12 +- uniconnect-app/app/page.tsx | 4 +- .../components/navigation/accessibility.tsx | 57 ++++++- .../components/navigation/main-nav.tsx | 2 +- .../contexts/accessibility-context.tsx | 106 +++++++++++++ uniconnect-app/tailwind.config.ts | 150 +++++++++--------- 8 files changed, 293 insertions(+), 134 deletions(-) create mode 100644 uniconnect-app/contexts/accessibility-context.tsx diff --git a/uniconnect-app/app/app/layout.tsx b/uniconnect-app/app/app/layout.tsx index 1034bf1..e465c1c 100644 --- a/uniconnect-app/app/app/layout.tsx +++ b/uniconnect-app/app/app/layout.tsx @@ -32,11 +32,7 @@ export default function AppLayout({ children }: { children: React.ReactNode }) { <div className='grid min-h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr]'> <div className='hidden border-r bg-muted/40 md:block'> <div className='flex h-full max-h-screen flex-col gap-8 lg:gap-12'> - <div className='flex h-16 items-center px-4 lg:h-[72px] lg:px-6'> - <Link href='/app' className=''> - <Logo className='h-8 w-auto' /> - </Link> - </div> + <div className='flex h-16 items-center px-4 lg:h-[32px] lg:px-6'></div> <div className='flex-1'> <nav className='grid items-start px-2 text-sm font-medium lg:px-4'> diff --git a/uniconnect-app/app/globals.css b/uniconnect-app/app/globals.css index edd81d5..23de7e2 100644 --- a/uniconnect-app/app/globals.css +++ b/uniconnect-app/app/globals.css @@ -5,57 +5,63 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; + --foreground: 20 14.3% 4.1%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; + --card-foreground: 20 14.3% 4.1%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 221.2 83.2% 53.3%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --popover-foreground: 20 14.3% 4.1%; + --primary: 47.9 95.8% 53.1%; + --primary-foreground: 26 83.3% 14.1%; + --secondary: 60 4.8% 95.9%; + --secondary-foreground: 24 9.8% 10%; + --muted: 60 4.8% 95.9%; + --muted-foreground: 25 5.3% 44.7%; + --accent: 60 4.8% 95.9%; + --accent-foreground: 24 9.8% 10%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 221.2 83.2% 53.3%; + --destructive-foreground: 60 9.1% 97.8%; + --border: 20 5.9% 90%; + --input: 20 5.9% 90%; + --ring: 20 14.3% 4.1%; --radius: 0.3rem; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + + --text-size-multiplier: 1; } - .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 217.2 91.2% 59.8%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 224.3 76.3% 48%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + .high-contrast { + --background: 0 0% 0%; + --foreground: 0 0% 100%; + --card: 0 0% 0%; + --card-foreground: 0 0% 100%; + --popover: 0 0% 0%; + --popover-foreground: 0 0% 100%; + --primary: 0 0% 100%; + --primary-foreground: 0 0% 0%; + --secondary: 0 0% 20%; + --secondary-foreground: 0 0% 100%; + --muted: 0 0% 20%; + --muted-foreground: 0 0% 90%; + --accent: 0 0% 20%; + --accent-foreground: 0 0% 100%; + --destructive: 0 100% 50%; + --destructive-foreground: 0 0% 100%; + --border: 0 0% 40%; + --input: 0 0% 40%; + --ring: 0 0% 100%; + + filter: invert(0.1) hue-rotate(240deg); + color: white !important; + + --chart-1: 0 100% 70%; + --chart-2: 120 100% 70%; + --chart-3: 240 100% 70%; + --chart-4: 60 100% 70%; + --chart-5: 300 100% 70%; } } @@ -66,4 +72,8 @@ body { @apply bg-background text-foreground; } + + html { + font-size: calc(100% * var(--text-size-multiplier)); + } } diff --git a/uniconnect-app/app/layout.tsx b/uniconnect-app/app/layout.tsx index c96db98..4400cbb 100644 --- a/uniconnect-app/app/layout.tsx +++ b/uniconnect-app/app/layout.tsx @@ -6,6 +6,8 @@ import './globals.css'; import Nav from '@/components/navigation/nav'; import AccessibilityMenu from '@/components/accessibility/accessibilityMenu'; +import { AccessibilityProvider } from '@/contexts/accessibility-context'; + const geistSans = localFont({ src: './fonts/GeistVF.woff', variable: '--font-geist-sans', @@ -30,13 +32,15 @@ export default function RootLayout({ }>) { return ( <html - className={`${geistSans.variable} ${geistMono.variable} antialiased`} + className={`${geistSans.variable} ${geistMono.variable} antialiased high-contrast`} lang='en' > <body className='relative'> - <AccessibilityMenu /> - <Nav /> - {children} + <AccessibilityProvider> + <AccessibilityMenu /> + <Nav /> + {children} + </AccessibilityProvider> </body> </html> ); diff --git a/uniconnect-app/app/page.tsx b/uniconnect-app/app/page.tsx index 3d6be58..7f348c1 100644 --- a/uniconnect-app/app/page.tsx +++ b/uniconnect-app/app/page.tsx @@ -37,7 +37,7 @@ export default function LandingPage() { {/* Feaures section */} <section className='w-full py-12 md:py-24 lg:py-32 bg-gray-100 dark:bg-gray-800'> <div className='container px-4 md:px-6 mx-auto'> - <h2 className='text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl text-center mb-8'> + <h2 className='text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl text-center mb-8 text-black'> Features </h2> <div className='grid gap-6 sm:grid-cols-2 lg:grid-cols-4'> @@ -130,7 +130,7 @@ export default function LandingPage() { <div className='container px-4 md:px-6 mx-auto'> <div className='flex flex-col items-center space-y-4 text-center'> <div className='space-y-2'> - <h2 className='text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl'> + <h2 className='text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl text-black'> Join Uniconnect Today </h2> <p className='mx-auto max-w-[700px] text-gray-500 md:text-xl dark:text-gray-400'> diff --git a/uniconnect-app/components/navigation/accessibility.tsx b/uniconnect-app/components/navigation/accessibility.tsx index dfa569f..4475b97 100644 --- a/uniconnect-app/components/navigation/accessibility.tsx +++ b/uniconnect-app/components/navigation/accessibility.tsx @@ -1,3 +1,5 @@ +'use client'; + import { AArrowDown, AArrowUp, ALargeSmall, Languages } from 'lucide-react'; import Container from '@/components/ui/container'; @@ -12,15 +14,41 @@ import { import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; +import { useAccessibility } from '@/contexts/accessibility-context'; +import { useState, useEffect } from 'react'; export default function Accessibility() { + const { + highContrast, + toggleHighContrast, + increaseTextSize, + decreaseTextSize, + resetTextSize, + } = useAccessibility(); + + const [language, setLanguage] = useState('english'); + + const [isHighContrast, setIsHighContrast] = useState(false); + + useEffect(() => { + setIsHighContrast(highContrast); + }, [highContrast]); + + const handleLanguageChange = (value: string) => { + setLanguage(value); + }; + return ( - <div className='bg-blue-100 border-b border-zinc-30'> - <Container className='py-4 justify-between hidden md:flex'> + <div className='bg-yellow-100 border-b border-zinc-30'> + <Container className='py-4 justify-between hidden md:flex'> <div className='flex items-center gap-x-2'> - <Languages /> - <Select defaultValue='english'> - <SelectTrigger className='w-[180px] font-medium border-muted-foreground'> + <Languages className='text-black' /> + <Select + defaultValue='english' + value={language} + onValueChange={handleLanguageChange} + > + <SelectTrigger className='w-[180px] font-medium border-black text-black'> <SelectValue /> </SelectTrigger> <SelectContent className='font-medium'> @@ -36,8 +64,17 @@ export default function Accessibility() { </div> <div className='flex items-center space-x-2'> - <Switch id='high-contrast-mode' /> - <Label htmlFor='high-contrast-mode' className='font-medium'> + <Switch + id='high-contrast-mode' + checked={isHighContrast} + onCheckedChange={() => { + toggleHighContrast(); + }} + /> + <Label + htmlFor='high-contrast-mode' + className='font-medium text-black' + > High Contrast </Label> </div> @@ -47,6 +84,8 @@ export default function Accessibility() { variant='outline' size='icon' className='border-muted-foreground' + onClick={decreaseTextSize} + aria-label='Decrease text size' > <AArrowDown /> </Button> @@ -54,6 +93,8 @@ export default function Accessibility() { variant='outline' size='icon' className='border-muted-foreground' + onClick={resetTextSize} + aria-label='Reset text size' > <ALargeSmall /> </Button> @@ -61,6 +102,8 @@ export default function Accessibility() { variant='outline' size='icon' className='border-muted-foreground' + onClick={increaseTextSize} + aria-label='Increase text size' > <AArrowUp /> </Button> diff --git a/uniconnect-app/components/navigation/main-nav.tsx b/uniconnect-app/components/navigation/main-nav.tsx index d19ed97..7102953 100644 --- a/uniconnect-app/components/navigation/main-nav.tsx +++ b/uniconnect-app/components/navigation/main-nav.tsx @@ -16,7 +16,7 @@ import { export default function MainNav() { return ( - <div className='bg-blue-100 border-b border-zinc-500'> + <div className='bg-yellow-100 border-b border-zinc-500'> <Container className='py-4 flex justify-between items-center'> <Link href='/'> <h1 className='flex items-center gap-x-2'> diff --git a/uniconnect-app/contexts/accessibility-context.tsx b/uniconnect-app/contexts/accessibility-context.tsx new file mode 100644 index 0000000..d83f2f1 --- /dev/null +++ b/uniconnect-app/contexts/accessibility-context.tsx @@ -0,0 +1,106 @@ +'use client'; + +import type React from 'react'; +import { createContext, useContext, useState, useEffect } from 'react'; + +type AccessibilityContextType = { + highContrast: boolean; + toggleHighContrast: () => void; + textSize: number; + increaseTextSize: () => void; + decreaseTextSize: () => void; + resetTextSize: () => void; +}; + +const AccessibilityContext = createContext< + AccessibilityContextType | undefined +>(undefined); + +export const AccessibilityProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const [highContrast, setHighContrast] = useState(false); + const [textSize, setTextSize] = useState(1); + + useEffect(() => { + const savedHighContrast = localStorage.getItem('highContrast') === 'true'; + const savedTextSize = Number.parseFloat( + localStorage.getItem('textSize') || '1' + ); + + setHighContrast(savedHighContrast); + setTextSize(savedTextSize); + + applyHighContrast(savedHighContrast); + applyTextSize(savedTextSize); + }, []); + + const applyHighContrast = (enabled: boolean) => { + if (enabled) { + document.documentElement.classList.add('high-contrast'); + } else { + document.documentElement.classList.remove('high-contrast'); + } + }; + + const applyTextSize = (size: number) => { + document.documentElement.style.setProperty( + '--text-size-multiplier', + size.toString() + ); + }; + + const toggleHighContrast = () => { + const newValue = !highContrast; + setHighContrast(newValue); + localStorage.setItem('highContrast', newValue.toString()); + applyHighContrast(newValue); + }; + + const increaseTextSize = () => { + const newSize = Math.min(textSize + 0.1, 1.5); // Max 1.5x + setTextSize(newSize); + localStorage.setItem('textSize', newSize.toString()); + applyTextSize(newSize); + }; + + const decreaseTextSize = () => { + const newSize = Math.max(textSize - 0.1, 0.8); // Min 0.8x + setTextSize(newSize); + localStorage.setItem('textSize', newSize.toString()); + applyTextSize(newSize); + }; + + const resetTextSize = () => { + setTextSize(1); + localStorage.setItem('textSize', '1'); + applyTextSize(1); + }; + + return ( + <AccessibilityContext.Provider + value={{ + highContrast, + toggleHighContrast, + textSize, + increaseTextSize, + decreaseTextSize, + resetTextSize, + }} + > + {children} + </AccessibilityContext.Provider> + ); +}; + +export const useAccessibility = () => { + const context = useContext(AccessibilityContext); + if (context === undefined) { + throw new Error( + 'useAccessibility must be used within an AccessibilityProvider' + ); + } + return context; +}; diff --git a/uniconnect-app/tailwind.config.ts b/uniconnect-app/tailwind.config.ts index da45954..f32dce7 100644 --- a/uniconnect-app/tailwind.config.ts +++ b/uniconnect-app/tailwind.config.ts @@ -8,81 +8,81 @@ const config: Config = { './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { - extend: { - fontFamily: { - sans: ['var(--font-geist-sans)'], - mono: ['var(--font-geist-mono)'] - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - } - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - keyframes: { - 'accordion-down': { - from: { - height: '0' - }, - to: { - height: 'var(--radix-accordion-content-height)' - } - }, - 'accordion-up': { - from: { - height: 'var(--radix-accordion-content-height)' - }, - to: { - height: '0' - } - } - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out' - } - } + extend: { + fontFamily: { + sans: ['var(--font-geist-sans)'], + mono: ['var(--font-geist-mono)'], + }, + colors: { + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + keyframes: { + 'accordion-down': { + from: { + height: '0', + }, + to: { + height: 'var(--radix-accordion-content-height)', + }, + }, + 'accordion-up': { + from: { + height: 'var(--radix-accordion-content-height)', + }, + to: { + height: '0', + }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, }, plugins: [require('tailwindcss-animate')], }; -- GitLab