diff --git a/frontend/README.md b/frontend/README.md
index 1a4e707..15e2506 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,4 +1,4 @@
-# Welcome to your Expo app 👋
+# Longhorn Studies
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
diff --git a/frontend/app/(tabs)/_layout.tsx b/frontend/app/(tabs)/_layout.tsx
index 54e11d0..97dab64 100644
--- a/frontend/app/(tabs)/_layout.tsx
+++ b/frontend/app/(tabs)/_layout.tsx
@@ -15,7 +15,9 @@ export default function TabLayout() {
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
- }}>
+ }}
+
+ >
- }>
+ }
+ >
Welcome!
@@ -74,11 +78,6 @@ export default function HomeScreen() {
app-example.
-
-
- Welcome to Nativewind!
-
-
);
}
diff --git a/frontend/app/(tabs)/test.tsx b/frontend/app/(tabs)/test.tsx
new file mode 100644
index 0000000..3976955
--- /dev/null
+++ b/frontend/app/(tabs)/test.tsx
@@ -0,0 +1,21 @@
+import { Image } from "expo-image";
+import { View } from "react-native";
+
+import StudySpotCard from "@/components/ui/study-spot-card";
+
+export default function HomeScreen() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/assets/fonts/Roboto-Flex.ttf b/frontend/assets/fonts/Roboto-Flex.ttf
new file mode 100644
index 0000000..2e5c2a2
Binary files /dev/null and b/frontend/assets/fonts/Roboto-Flex.ttf differ
diff --git a/frontend/components/ui/icons/bookmark.tsx b/frontend/components/ui/icons/bookmark.tsx
new file mode 100644
index 0000000..af8a3df
--- /dev/null
+++ b/frontend/components/ui/icons/bookmark.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+
+import type { IconProps } from ".";
+
+import { cssInterop } from "nativewind";
+
+// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
+cssInterop(Svg, {
+ className: {
+ target: "style",
+ },
+});
+
+export default function BookmarkIcon({
+ color = "#1A2024",
+ size = 24,
+ ...props
+}: IconProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/components/ui/icons/caret-right.tsx b/frontend/components/ui/icons/caret-right.tsx
new file mode 100644
index 0000000..eb2690f
--- /dev/null
+++ b/frontend/components/ui/icons/caret-right.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+
+import type { IconProps } from ".";
+
+import { cssInterop } from "nativewind";
+
+// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
+cssInterop(Svg, {
+ className: {
+ target: "style",
+ },
+});
+
+export default function CaretRightIcon({
+ color = "#1A2024",
+ size = 24,
+ ...props
+}: IconProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/components/ui/icons/clock.tsx b/frontend/components/ui/icons/clock.tsx
new file mode 100644
index 0000000..a391d72
--- /dev/null
+++ b/frontend/components/ui/icons/clock.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+
+import type { IconProps } from ".";
+
+import { cssInterop } from "nativewind";
+
+// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
+cssInterop(Svg, {
+ className: {
+ target: "style",
+ },
+});
+
+export default function ClockIcon({
+ color = "#1A2024",
+ size = 24,
+ ...props
+}: IconProps) {
+ return (
+
+ );
+}
diff --git a/frontend/components/ui/icons/index.tsx b/frontend/components/ui/icons/index.tsx
new file mode 100644
index 0000000..bc0dc84
--- /dev/null
+++ b/frontend/components/ui/icons/index.tsx
@@ -0,0 +1,12 @@
+import { SvgProps } from "react-native-svg";
+
+import ClockIcon from "./clock";
+import BookmarkIcon from "./bookmark";
+import CaretRightIcon from "./caret-right";
+import WalkingIcon from "./walking";
+export interface IconProps extends SvgProps {
+ color?: string;
+ size?: number;
+}
+
+export { ClockIcon, BookmarkIcon, CaretRightIcon, WalkingIcon };
diff --git a/frontend/components/ui/icons/walking.tsx b/frontend/components/ui/icons/walking.tsx
new file mode 100644
index 0000000..b7093c3
--- /dev/null
+++ b/frontend/components/ui/icons/walking.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+
+import type { IconProps } from ".";
+
+import { cssInterop } from "nativewind";
+
+// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
+cssInterop(Svg, {
+ className: {
+ target: "style",
+ },
+});
+
+export default function WalkingIcon({
+ color = "#94A3B8",
+ size = 14,
+ ...props
+}: IconProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/components/ui/open-status.tsx b/frontend/components/ui/open-status.tsx
new file mode 100644
index 0000000..d36dca9
--- /dev/null
+++ b/frontend/components/ui/open-status.tsx
@@ -0,0 +1,29 @@
+import { View, Text } from "react-native";
+import { ClockIcon } from "./icons";
+
+type OpenStatusType = {
+ openStatus: "open" | "closing" | "closed";
+ hours?: string[];
+};
+
+export default function OpenStatus({ openStatus, hours }: OpenStatusType) {
+ const color = `text-status-${openStatus}`;
+ return (
+
+ {/** ENSURES COLORS LOAD PROPERLY */}
+
+
+ {openStatus === "open" && "Open"}
+ {openStatus === "closing" && "Closing"}
+ {openStatus === "closed" && "Closed"}
+
+ {hours && openStatus !== "closed" && until {hours[1]}}
+ {hours && openStatus === "closed" && until {hours[0]}}
+
+ );
+}
diff --git a/frontend/components/ui/study-spot-card/index.tsx b/frontend/components/ui/study-spot-card/index.tsx
new file mode 100644
index 0000000..b6147d4
--- /dev/null
+++ b/frontend/components/ui/study-spot-card/index.tsx
@@ -0,0 +1,71 @@
+import { Image } from "expo-image";
+
+import { View, Text, TouchableOpacity } from "react-native";
+
+import OpenStatus from "../open-status";
+import { Tag } from "../tags";
+
+import { BookmarkIcon, CaretRightIcon, WalkingIcon } from "../icons";
+
+const blurhash =
+ "|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[";
+
+type StudySpotCardType = {
+ locationName: string;
+ buildingName: string;
+ location: string; // TODO: get proper type
+ hours: string[];
+ tags: string[];
+};
+export default function StudySpotCard({
+ locationName,
+ buildingName,
+ hours,
+ tags,
+}: StudySpotCardType) {
+ return (
+
+
+
+ console.log("Bookmark tapped!")}
+ hitSlop={{ top: 15, bottom: 15, left: 15, right: 15 }}
+ >
+
+
+
+
+
+
+
+ {buildingName}
+
+ {locationName}
+
+
+
+
+ 0.1 mi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/components/ui/tags/index.tsx b/frontend/components/ui/tags/index.tsx
new file mode 100644
index 0000000..c56afb1
--- /dev/null
+++ b/frontend/components/ui/tags/index.tsx
@@ -0,0 +1,13 @@
+import { View, Text } from "react-native";
+
+type TagProps = {
+ tag: string;
+};
+
+export function Tag({ tag }: TagProps) {
+ return (
+
+ {tag}
+
+ );
+}
diff --git a/frontend/package.json b/frontend/package.json
index 62314bc..5aea7d9 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -34,8 +34,9 @@
"react-native": "0.81.5",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "^4.1.1",
- "react-native-safe-area-context": "^5.4.0",
+ "react-native-safe-area-context": "^5.6.0",
"react-native-screens": "~4.16.0",
+ "react-native-svg": "15.12.1",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1",
"tailwindcss": "^3.4.19"
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index e4fb158..1c71e6b 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -2,7 +2,38 @@
module.exports = {
content: ['./components/**/*.{js,jsx,ts,tsx}', './app/**/*.{js,jsx,ts,tsx}'],
theme: {
- extend: {},
+ extend: {
+ colors: {
+ // Primary
+ 'white': '#FFFFFF',
+ 'burnt-orange': '#BF5700', // study spot building names/abbrevs
+ 'main-text': '#333F48', // Used with all main texts
+ 'card-border': '#BAC1CC', // All card borders
+ 'gray-text': '#94A3B8', // Gray text
+
+ // Opaque colors
+ // Note: You can also use Tailwind's built-in opacity modifier like `bg-brand-orange/50`
+ 'active-filter': 'rgba(191, 87, 0, 0.5)', // #BF5700 at 0.5 opacity
+ 'saved-bg': 'rgba(214, 210, 196, 0.5)', // #D6D2C4 at 0.5 opacity
+
+ // Hours Color States
+ 'status-open': '#579D42',
+ 'status-closing': '#F8971F',
+ 'status-closed': '#D10000',
+ },
+ fontFamily: {
+ roboto: ['"Roboto Flex"', 'sans-serif'],
+ },
+ fontSize: {
+ 'heading': '25.63px', // Heading
+ 'details-header': '22.78px', // Study Spot Details Header
+ 'filter-header': '20.25px', // Filter Header
+ 'base-16': '16px', // Filter Option, Details, Search Bar
+ 'spot-name': '14.22px', // Study Spot Name, Details
+ 'status': '12.64px', // Open Status + Distance
+ 'tag': '10px', // Tags
+ }
+ },
},
presets: [require('nativewind/preset')],
plugins: [],