5025251096_Gabriel Mesly Managam Siahaan_Tugas CSS
Tampilan Web , telah ditambahkan saya sendiri ke tabel
Index.html
<!doctype html>
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Kelola data mahasiswa dengan mudah menggunakan Student Management."
/>
<title>Student Management</title>
<link rel="stylesheet" href="styles.css" />
<script src="app.js" defer></script>
</head>
<body>
<header class="topbar">
<a class="brand" href="#home" aria-label="Student Management — Home">
<span class="brand-icon" aria-hidden="true">
<svg viewBox="0 0 48 48" fill="none">
<path d="M3 17.5 24 7l21 10.5L24 28 3 17.5Z" fill="currentColor" />
<path d="M11 22.5V33c7.5 6 18.5 6 26 0V22.5L24 29l-13-6.5Z" fill="currentColor" />
<path d="M43 20v12" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
</svg>
</span>
<span>Student Management</span>
</a>
<nav class="main-nav" aria-label="Navigasi utama">
<a href="#home">Home</a>
<a href="#students">Students</a>
<a href="#about">About</a>
</nav>
<button class="profile-button" type="button" aria-label="Profil pengguna">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 12a4.3 4.3 0 1 0 0-8.6 4.3 4.3 0 0 0 0 8.6Zm0 2c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5Z" fill="currentColor" />
</svg>
</button>
</header>
<main class="dashboard" id="home">
<section class="panel form-panel" aria-labelledby="form-heading">
<h1 id="form-heading">Form Student</h1>
<form id="student-form">
<label for="nim">NIM</label>
<input
id="nim"
name="nim"
type="text"
placeholder="Masukkan NIM"
autocomplete="off"
required
/>
<label for="name">Nama Lengkap</label>
<input
id="name"
name="name"
type="text"
placeholder="Masukkan nama lengkap"
autocomplete="name"
required
/>
<label for="major">Jurusan</label>
<select id="major" name="major" required>
<option value="" disabled selected>Pilih Jurusan</option>
<option>Informatika</option>
<option>Sistem Informasi</option>
<option>Teknik Komputer</option>
<option>Manajemen</option>
<option>Akuntansi</option>
<option>Teknik Elektro</option>
</select>
<label for="email">Email</label>
<input
id="email"
name="email"
type="email"
placeholder="Masukkan email"
autocomplete="email"
required
/>
<div class="form-actions">
<button class="button button-primary" type="submit">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3h12l4 4v14H3V3h2Zm2 2v5h10V5H7Zm0 14h10v-6H7v6Zm8-13h2v3h-2V6Z" fill="currentColor" /></svg>
<span id="submit-label">Simpan</span>
</button>
<button class="button button-muted" id="cancel-button" type="button">Batal</button>
<button class="button button-danger" id="reset-button" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4a8 8 0 0 1 7.5 5H17l4 4 4-4h-3.3A10 10 0 0 0 4.1 7.3l2.1 2.1A7.9 7.9 0 0 1 12 4Zm-7.5 7-4 4h3.3A10 10 0 0 0 19.9 17l-2.1-2.1A8 8 0 0 1 4.5 15H7l-2.5-4Z" fill="currentColor" transform="translate(1 1) scale(.92)" /></svg>
<span>Reset</span>
</button>
</div>
<p class="form-message" id="form-message" role="status" aria-live="polite"></p>
</form>
</section>
<section class="panel table-panel" id="students" aria-labelledby="students-heading">
<div class="table-heading">
<h2 id="students-heading">Data Mahasiswa</h2>
<label class="search-box" for="student-search">
<span class="visually-hidden">Cari mahasiswa</span>
<input id="student-search" type="search" placeholder="Cari mahasiswa..." />
<span class="search-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><circle cx="10.8" cy="10.8" r="6.8" fill="none" stroke="currentColor" stroke-width="2.5" /><path d="m16 16 5 5" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" /></svg>
</span>
</label>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th scope="col">No</th>
<th scope="col">NIM</th>
<th scope="col">Nama</th>
<th scope="col">Jurusan</th>
<th scope="col">Email</th>
<th scope="col">Aksi</th>
</tr>
</thead>
<tbody id="student-rows"></tbody>
</table>
<p class="empty-state" id="empty-state" hidden>Mahasiswa tidak ditemukan.</p>
</div>
<nav class="pagination" id="pagination" aria-label="Halaman data mahasiswa"></nav>
</section>
</main>
<footer id="about">Student Management <span aria-hidden="true">·</span> Kelola data mahasiswa dengan mudah</footer>
</body>
</html>
STYLES .css
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap");
:root {
font-family: Inter, "Segoe UI", Arial, sans-serif;
color: #17243a;
background: #eaf1f8;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-optical-sizing: auto;
font-weight: 400;
--blue: #1769e8;
--navy: #123b66;
--border: #dce4ed;
}
* {
box-sizing: border-box;
}
body {
min-width: 320px;
min-height: 100vh;
margin: 0;
background:
radial-gradient(ellipse at 15% 0%, rgba(206, 226, 249, 0.75), transparent 38rem),
#edf2f8;
}
button,
input,
select {
font: inherit;
}
.topbar {
min-height: 82px;
display: flex;
align-items: center;
gap: 32px;
padding: 0 clamp(20px, 4vw, 54px);
color: #fff;
background: linear-gradient(110deg, #123b66, #174675);
box-shadow: 0 5px 18px rgba(22, 51, 82, 0.14);
}
.brand {
display: inline-flex;
align-items: center;
gap: 15px;
color: inherit;
font-size: clamp(1.1rem, 2vw, 1.45rem);
font-weight: 750;
letter-spacing: -0.045em;
text-decoration: none;
white-space: nowrap;
}
.brand-icon {
display: grid;
width: 44px;
height: 44px;
place-items: center;
}
.brand-icon svg {
width: 100%;
height: 100%;
}
.main-nav {
display: flex;
align-items: center;
gap: clamp(22px, 4vw, 52px);
margin-left: auto;
}
.main-nav a {
color: rgba(255, 255, 255, 0.91);
font-size: 0.96rem;
font-weight: 600;
text-decoration: none;
transition: color 160ms ease;
}
.main-nav a:hover,
.main-nav a:focus-visible {
color: #b9d7ff;
}
.profile-button {
display: grid;
width: 42px;
height: 42px;
padding: 8px;
place-items: center;
color: #174675;
background: #f3f7fc;
border: 0;
border-radius: 50%;
cursor: pointer;
}
.profile-button svg {
width: 100%;
height: 100%;
}
.dashboard {
display: grid;
grid-template-columns: minmax(280px, 0.82fr) minmax(0, 1.65fr);
align-items: stretch;
gap: 20px;
width: min(1540px, 100%);
margin: 0 auto;
padding: 24px clamp(18px, 2.4vw, 32px) 26px;
}
.panel {
min-width: 0;
padding: clamp(22px, 2.5vw, 34px);
background: rgba(255, 255, 255, 0.96);
border: 1px solid rgba(216, 226, 237, 0.8);
border-radius: 20px;
box-shadow: 0 12px 35px rgba(35, 62, 91, 0.07);
}
.form-panel h1,
.table-heading h2 {
margin: 0;
color: #17243a;
font-size: clamp(1.24rem, 2vw, 1.55rem);
font-weight: 750;
letter-spacing: -0.045em;
}
#student-form {
display: flex;
flex-direction: column;
margin-top: 24px;
}
#student-form label {
margin: 0 0 8px;
color: #273449;
font-size: 0.96rem;
font-weight: 600;
}
#student-form input,
#student-form select {
width: 100%;
min-height: 47px;
margin: 0 0 18px;
padding: 0 14px;
color: #25334a;
background-color: #fff;
border: 1px solid #cfd9e4;
border-radius: 9px;
outline: none;
transition: border-color 160ms ease, box-shadow 160ms ease;
}
#student-form input::placeholder,
.search-box input::placeholder {
color: #97a3b3;
}
#student-form select {
cursor: pointer;
}
#student-form input:focus,
#student-form select:focus,
.search-box:focus-within {
border-color: #4a91f5;
box-shadow: 0 0 0 3px rgba(23, 105, 232, 0.12);
}
.form-actions {
display: flex;
flex-wrap: wrap;
gap: 9px;
margin-top: 12px;
}
.button {
display: inline-flex;
min-height: 46px;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 14px;
color: #fff;
border: 0;
border-radius: 9px;
cursor: pointer;
font-weight: 650;
transition: transform 150ms ease, filter 150ms ease, box-shadow 150ms ease;
}
.button:hover {
filter: brightness(1.06);
transform: translateY(-1px);
box-shadow: 0 5px 13px rgba(30, 60, 95, 0.16);
}
.button:active {
transform: translateY(0);
}
.button svg {
width: 18px;
height: 18px;
}
.button-primary {
background: linear-gradient(135deg, #287cf2, #1262dc);
}
.button-muted {
background: #71839a;
}
.button-danger {
background: #ed414b;
}
.form-message {
min-height: 20px;
margin: 12px 0 0;
color: #167448;
font-size: 0.84rem;
}
.form-message.is-error {
color: #c02c38;
}
.table-panel {
display: flex;
flex-direction: column;
padding: clamp(22px, 2.5vw, 34px);
}
.table-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
margin-bottom: 23px;
}
.search-box {
display: flex;
width: min(100%, 270px);
min-height: 44px;
align-items: center;
overflow: hidden;
border: 1px solid #d5dee8;
border-radius: 9px;
transition: border-color 160ms ease, box-shadow 160ms ease;
}
.search-box input {
width: 100%;
min-width: 0;
height: 42px;
padding: 0 13px;
color: #25334a;
background: transparent;
border: 0;
outline: 0;
}
.search-box input:focus {
box-shadow: none;
}
.search-icon {
display: grid;
width: 48px;
min-width: 48px;
height: 44px;
place-items: center;
color: #53657b;
border-left: 1px solid #e1e7ef;
}
.search-icon svg {
width: 19px;
height: 19px;
}
.table-wrap {
width: 100%;
overflow-x: auto;
border: 1px solid #dce4ed;
border-radius: 9px;
}
table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
th,
td {
padding: 11px 13px;
border-right: 1px solid #e0e6ed;
border-bottom: 1px solid #e0e6ed;
white-space: nowrap;
}
th:last-child,
td:last-child {
border-right: 0;
}
tbody tr:last-child td {
border-bottom: 0;
}
th {
height: 52px;
color: #fff;
background: linear-gradient(120deg, #2879ed, #1260dc);
font-size: 0.9rem;
font-weight: 650;
}
td {
height: 52px;
color: #28364b;
background: #fff;
font-size: 0.88rem;
}
tbody tr:nth-child(even) td {
background: #f8fafc;
}
tbody tr:hover td {
background: #eff6ff;
}
th:first-child,
td:first-child {
width: 54px;
text-align: center;
}
th:last-child,
td:last-child {
width: 110px;
text-align: center;
}
.row-actions {
display: flex;
justify-content: center;
gap: 7px;
}
.icon-button {
display: inline-grid;
width: 35px;
height: 35px;
padding: 8px;
place-items: center;
color: #fff;
border: 0;
border-radius: 7px;
cursor: pointer;
transition: filter 150ms ease, transform 150ms ease;
}
.icon-button:hover {
filter: brightness(1.1);
transform: translateY(-1px);
}
.icon-button svg {
width: 100%;
height: 100%;
}
.edit-button {
background: #2377eb;
}
.delete-button {
background: #ee424d;
}
.empty-state {
margin: 0;
padding: 28px 16px;
color: #68788e;
text-align: center;
}
.pagination {
display: flex;
justify-content: flex-end;
gap: 0;
margin-top: 24px;
}
.page-button {
display: grid;
min-width: 42px;
height: 42px;
padding: 0 11px;
place-items: center;
color: #34445a;
background: #fff;
border: 1px solid #dce4ed;
border-right: 0;
cursor: pointer;
font-weight: 550;
transition: background 150ms ease, color 150ms ease;
}
.page-button:first-child {
border-radius: 8px 0 0 8px;
}
.page-button:last-child {
border-right: 1px solid #dce4ed;
border-radius: 0 8px 8px 0;
}
.page-button:hover:not(:disabled) {
color: var(--blue);
background: #eef5ff;
}
.page-button.is-active {
color: #fff;
background: linear-gradient(135deg, #2879ed, #1260dc);
border-color: #1769e8;
}
.page-button:disabled {
color: #9ba7b5;
cursor: not-allowed;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
footer {
padding: 0 20px 22px;
color: #8390a0;
font-size: 0.8rem;
text-align: center;
}
footer span {
padding: 0 4px;
}
@media (min-width: 1200px) {
.dashboard {
min-height: calc(100vh - 112px);
}
}
@media (max-width: 900px) {
.dashboard {
grid-template-columns: 1fr;
}
.form-panel {
order: 1;
}
.table-panel {
order: 2;
}
}
@media (max-width: 560px) {
.topbar {
min-height: 72px;
gap: 12px;
padding: 0 16px;
}
.brand {
gap: 8px;
font-size: 1rem;
}
.brand-icon {
width: 34px;
height: 34px;
}
.main-nav {
gap: 12px;
}
.main-nav a {
font-size: 0.76rem;
}
.profile-button {
display: none;
}
.dashboard {
gap: 14px;
padding: 14px 12px 18px;
}
.panel {
padding: 20px 16px;
border-radius: 15px;
}
.table-heading {
align-items: stretch;
flex-direction: column;
margin-bottom: 16px;
}
.search-box {
width: 100%;
}
.pagination {
justify-content: center;
margin-top: 18px;
}
.page-button {
min-width: 36px;
height: 38px;
padding: 0 8px;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}
app.js
const initialStudents = [
{ nim: "231001", name: "Andi Pratama", major: "Informatika", email: "andi@mail.com" },
{ nim: "231002", name: "Siti Aisyah", major: "Sistem Informasi", email: "siti@mail.com" },
{ nim: "231003", name: "Budi Santoso", major: "Teknik Komputer", email: "budi@mail.com" },
{ nim: "231004", name: "Nina Marlina", major: "Manajemen", email: "nina@mail.com" },
{ nim: "231005", name: "Rizky Pratama", major: "Informatika", email: "rizky@mail.com" },
];
const pageSize = 5;
let students = [...initialStudents];
let currentPage = 1;
let editingNim = null;
const form = document.querySelector("#student-form");
const formHeading = document.querySelector("#form-heading");
const submitLabel = document.querySelector("#submit-label");
const formMessage = document.querySelector("#form-message");
const searchInput = document.querySelector("#student-search");
const rows = document.querySelector("#student-rows");
const emptyState = document.querySelector("#empty-state");
const pagination = document.querySelector("#pagination");
function getFilteredStudents() {
const query = searchInput.value.trim().toLocaleLowerCase("id");
if (!query) return students;
return students.filter((student) =>
[student.nim, student.name, student.major, student.email]
.some((value) => value.toLocaleLowerCase("id").includes(query)),
);
}
function makeIcon(path) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "0 0 24 24");
svg.setAttribute("aria-hidden", "true");
const shape = document.createElementNS("http://www.w3.org/2000/svg", "path");
shape.setAttribute("d", path);
shape.setAttribute("fill", "currentColor");
svg.append(shape);
return svg;
}
function renderPagination(totalPages) {
pagination.replaceChildren();
if (totalPages <= 1) return;
const addPageButton = (label, page, { active = false, disabled = false, ariaLabel } = {}) => {
const button = document.createElement("button");
button.type = "button";
button.className = `page-button${active ? " is-active" : ""}`;
button.textContent = label;
button.disabled = disabled;
if (ariaLabel) button.setAttribute("aria-label", ariaLabel);
if (active) button.setAttribute("aria-current", "page");
button.addEventListener("click", () => {
currentPage = page;
renderStudents();
});
pagination.append(button);
};
addPageButton("«", 1, { disabled: currentPage === 1, ariaLabel: "Halaman pertama" });
addPageButton("‹", currentPage - 1, { disabled: currentPage === 1, ariaLabel: "Halaman sebelumnya" });
const visiblePages = Math.min(totalPages, 5);
let firstPage = Math.max(1, currentPage - 2);
firstPage = Math.min(firstPage, totalPages - visiblePages + 1);
for (let page = firstPage; page < firstPage + visiblePages; page += 1) {
addPageButton(String(page), page, { active: page === currentPage });
}
addPageButton("›", currentPage + 1, { disabled: currentPage === totalPages, ariaLabel: "Halaman berikutnya" });
addPageButton("»", totalPages, { disabled: currentPage === totalPages, ariaLabel: "Halaman terakhir" });
}
function renderStudents() {
const filteredStudents = getFilteredStudents();
const totalPages = Math.ceil(filteredStudents.length / pageSize);
currentPage = Math.min(Math.max(currentPage, 1), Math.max(totalPages, 1));
const firstIndex = (currentPage - 1) * pageSize;
const pageStudents = filteredStudents.slice(firstIndex, firstIndex + pageSize);
rows.replaceChildren();
pageStudents.forEach((student, index) => {
const row = document.createElement("tr");
[String(firstIndex + index + 1), student.nim, student.name, student.major, student.email].forEach((value) => {
const cell = document.createElement("td");
cell.textContent = value;
row.append(cell);
});
const actionCell = document.createElement("td");
const actions = document.createElement("div");
actions.className = "row-actions";
const editButton = document.createElement("button");
editButton.type = "button";
editButton.className = "icon-button edit-button";
editButton.setAttribute("aria-label", `Edit ${student.name}`);
editButton.append(makeIcon("M3 17.25V21h3.75L17.8 9.95l-3.75-3.75L4 16.25l-1 1ZM20.7 7.05a1 1 0 0 0 0-1.4l-2.35-2.35a1 1 0 0 0-1.4 0l-1.83 1.83 3.75 3.75 1.83-1.83Z"));
editButton.addEventListener("click", () => beginEdit(student));
const deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.className = "icon-button delete-button";
deleteButton.setAttribute("aria-label", `Hapus ${student.name}`);
deleteButton.append(makeIcon("M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12ZM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4Z"));
deleteButton.addEventListener("click", () => removeStudent(student));
actions.append(editButton, deleteButton);
actionCell.append(actions);
row.append(actionCell);
rows.append(row);
});
emptyState.hidden = filteredStudents.length > 0;
renderPagination(totalPages);
}
function showMessage(message, isError = false) {
formMessage.textContent = message;
formMessage.classList.toggle("is-error", isError);
}
function clearForm() {
form.reset();
editingNim = null;
formHeading.textContent = "Form Student";
submitLabel.textContent = "Simpan";
showMessage("");
}
function beginEdit(student) {
editingNim = student.nim;
form.elements.nim.value = student.nim;
form.elements.name.value = student.name;
form.elements.major.value = student.major;
form.elements.email.value = student.email;
formHeading.textContent = "Edit Mahasiswa";
submitLabel.textContent = "Simpan Perubahan";
showMessage(`Sedang mengedit data ${student.name}.`);
form.elements.name.focus();
}
function removeStudent(student) {
students = students.filter((item) => item.nim !== student.nim);
if (editingNim === student.nim) clearForm();
showMessage(`Data ${student.name} berhasil dihapus.`);
renderStudents();
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(form);
const student = {
nim: String(formData.get("nim")).trim(),
name: String(formData.get("name")).trim(),
major: String(formData.get("major")).trim(),
email: String(formData.get("email")).trim(),
};
const duplicate = students.some((item) => item.nim === student.nim && item.nim !== editingNim);
if (duplicate) {
showMessage("NIM tersebut sudah terdaftar. Gunakan NIM yang berbeda.", true);
form.elements.nim.focus();
return;
}
if (editingNim) {
students = students.map((item) => item.nim === editingNim ? student : item);
showMessage(`Data ${student.name} berhasil diperbarui.`);
} else {
students.unshift(student);
showMessage(`Data ${student.name} berhasil ditambahkan.`);
}
clearForm();
currentPage = 1;
renderStudents();
});
document.querySelector("#cancel-button").addEventListener("click", clearForm);
document.querySelector("#reset-button").addEventListener("click", clearForm);
searchInput.addEventListener("input", () => {
currentPage = 1;
renderStudents();
});
renderStudents();
CS
Komentar
Posting Komentar