Skip to content
Open
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
54 changes: 32 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# 📊 Project: Simple API 2

### Goal: Display data returned from an api

### How to submit your code for review:

- Fork and clone this repo
- Create a new branch called answer
- Checkout answer branch
- Push to your fork
- Issue a pull request
- Your pull request description should contain the following:
- (1 to 5 no 3) I completed the challenge
- (1 to 5 no 3) I feel good about my code
- Anything specific on which you want feedback!

Example:
```
I completed the challenge: 5
I feel good about my code: 4
I'm not sure if my constructors are setup cleanly...
```
# ☕ Here for Advice — Advice API App

A calm, cozy space where you can type a **topic** or tap **Random** to receive a bit of wisdom from the **Advice Slip API**.
Built with soft chocolate-nude tones to feel comforting, grounded, and easy on the eyes — like a warm note passed across the table.

[Link to project](https://simple-advice-api.vercel.app/)
![screenshot](img/advice.png "Here for Advice — Advice API App")

---

## How It’s Made:
**Tech used:** HTML, CSS, JavaScript

Here for Advice fetches quotes from the Advice Slip API and displays them on a note-style card once you click **Get Advice** or **Random**.
The interface stays minimal and centered, keeping the focus on the message while blending warm tones and soft gradients for a relaxing atmosphere.

---

## Optimizations
- Add a fade-in transition when advice appears.
- Include a short loading state or animation between fetches.
- Let users save favorite advice notes locally.
- Add a toggle for light/dark or seasonal color themes.
- Display a timestamp for when each note was received.

---

## Lessons Learned
- A minimal layout can still feel emotionally engaging through color and spacing.
- Adding small UX touches — like hiding content until it’s ready — makes the experience more intentional.
- Warm tones and subtle design choices can set the emotional tone of a project.
- Even the simplest API can inspire thoughtful, finished design.
196 changes: 196 additions & 0 deletions css/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/* css/styles.css */
* {
box-sizing: border-box;
}

html,
body {
height: 100%;
margin: 0;
}

body {
font-family: "Inter", Arial, Helvetica, sans-serif;
color: #fdf6ec;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
padding: 0 20px;
background: linear-gradient(135deg, #2c1a0f 0%, #4e2e1a 40%, #8b5a2b 70%, #c8a27a 100%);
background-attachment: fixed;
text-align: center;
}

body::before {
content: "";
position: fixed;
inset: 0;
background:
radial-gradient(circle at 50% 60%, rgba(255, 240, 200, 0.05), transparent 60%),
radial-gradient(circle at 10% 20%, rgba(255, 220, 160, 0.08), transparent 50%),
radial-gradient(circle at 90% 80%, rgba(180, 90, 30, 0.1), transparent 50%);
z-index: 0;
pointer-events: none;
}

.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
max-width: 480px;
width: 100%;
background: rgba(50, 30, 20, 0.8);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 14px;
padding: 32px 24px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
}


h1 {
font-family: "Playfair Display", Georgia, serif;
font-size: 2.4rem;
font-weight: 700;
margin-bottom: 4px;
color: #ffe9c4;
}

.subtitle {
color: #f4caa1;
font-size: 1rem;
margin-bottom: 22px;
}


input[type="text"] {
width: 100%;
padding: 12px;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 10px;
background: rgba(255, 255, 255, 0.08);
color: #fff6e5;
font-size: 1rem;
margin-bottom: 20px;
}

input::placeholder {
color: #e7ccb2;
}

input:focus {
border-color: #e0b97f;
outline: none;
}

.buttons {
display: flex;
justify-content: center;
gap: 18px;
margin-bottom: 15px;
}


button {
background: linear-gradient(180deg, rgba(224, 185, 127, 0.25), rgba(143, 97, 60, 0.18));
color: #fff8ef;
border: 1px solid rgba(255, 255, 255, 0.25);
padding: 12px 24px;
border-radius: 10px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s ease, transform 0.1s ease;
}

button:hover {
background: rgba(235, 191, 136, 0.35);
}

button:active {
transform: translateY(1px);
}

#random {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.18);
}

/* output */
/* chocolate paper note */
#output {
margin-top: 32px;
font-size: 1.06rem;
line-height: 1.6;
color: #fff2dd;
position: relative;
display: inline-block;
max-width: 42rem;
padding: 12px 18px;
border-radius: 10px;
border: 1px solid rgba(36, 23, 14, 0.6);

/* layered “paper” style idea from ChatGPT */
background:
/* subtle fibers */
repeating-linear-gradient(8deg,
rgba(255, 240, 200, 0.06) 0px,
rgba(255, 240, 200, 0.06) 2px,
rgba(0, 0, 0, 0.0) 3px,
rgba(0, 0, 0, 0.0) 6px),
linear-gradient(180deg, #5a3b26, #6b472f);
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35),
inset 0 1px 0 rgba(255, 255, 255, 0.08);
opacity: 0;
transform: translateY(6px) scale(0.99);
transition: opacity .25s ease, transform .25s ease;
}

/* “washi tape” style idea from ChatGPT */
#output::before {
content: "";
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%) rotate(-2deg);
width: 74px;
height: 16px;
border-radius: 4px;
background: linear-gradient(180deg, #e8c8a5, #d9b791);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
opacity: 0.95;
}

#output::after {
content: "";
position: absolute;
inset: -10px;
border-radius: 14px;
background: radial-gradient(circle at 50% 80%,
rgba(255, 230, 180, 0.15),
transparent 70%);
z-index: -1;
filter: blur(8px);
}

#output.show {
opacity: 1;
transform: translateY(0) scale(1);
position: relative;
}


@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(5px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

Binary file added img/advice.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/styles.css" />
</head>
<body>

<main class="shell">
<header class="top">
<h1>Here for advice?</h1>
<p class="sub">Take a breath, type a topic, and we’ll pull a little wisdom for you.</p>
</header>

<section class="panel" role="form" aria-label="Advice search">
<div class="row">
<input id="topic" type="text" placeholder="Type a single word (e.g., love)" autocomplete="off" />
<button id="search" class="btn">Get Advice</button>
<button id="random" class="btn btn-ghost">Random</button>
</div>

<div class="output" id="output"></div>
</section>
</main>

<script src="js/main.js"></script>
</body>
</html>
68 changes: 68 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// set output variable
const output = document.querySelector('#output');

// grab input so we can clear it on Random
const inputEl = document.querySelector('input');

// start hidden so it only appears after a result
output.style.display = 'none';

// input
document.querySelector('button').addEventListener('click', () => {
const query = document.querySelector('input').value;
if (!query) {
output.textContent = 'Please type a word first.';
output.style.display = 'inline-block';
output.classList.add('show');
return;
}
getAdvice();
});

// create function to get data from database
function getAdvice() {
const query = document.querySelector('input').value;
const url = `https://api.adviceslip.com/advice/search/${query}`;

fetch(url)
.then(res => res.json())
.then(data => {
if (data.slips && data.slips[0]) {
output.textContent = data.slips[0].advice;
output.style.display = 'inline-block';
// retrigger the pop-in
output.classList.remove('show');
requestAnimationFrame(() => output.classList.add('show'));
}
})
.catch(err => {
output.textContent = 'Could not fetch advice.';
output.style.display = 'inline-block';
console.error(err);
});
}

// listen for when random button is clicked
document.querySelector('#random').addEventListener('click', getRandomAdvice);

// create function for random advice
function getRandomAdvice() {
// NEW: clear the input so it doesn’t look stale
if (inputEl) inputEl.value = '';

const randomUrl = "https://api.adviceslip.com/advice";
fetch(randomUrl)
.then(res => res.json())
.then(data => {
if (data && data.slip && data.slip.advice) {
output.textContent = data.slip.advice;
output.style.display = 'inline-block';
output.classList.remove('show');
requestAnimationFrame(() => output.classList.add('show'));
}
})
.catch(() => {
output.textContent = 'Could not fetch advice.';
output.style.display = 'inline-block';
});
}