New version. Add Material UI implementation with theme customization, about dialog, and news system

This commit is contained in:
VlastikYoutubeKo 2025-05-20 21:40:17 +02:00
commit 6adca9741f
2 changed files with 890 additions and 183 deletions

View file

@ -1,10 +1,46 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MNotes</title> <title>MNotes</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/material-components-web/14.0.0/material-components-web.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,1,0">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap">
<style>
:root {
/* Material 3 (You/Expressive) Colors - Light Theme */
--primary-color: #006A6A;
--on-primary-color: #FFFFFF;
--primary-container-color: #6FF7F7;
--on-primary-container-color: #002020;
--secondary-color: #4A6363;
--on-secondary-color: #FFFFFF;
--secondary-container-color: #CCE8E7;
--on-secondary-container-color: #051F1F;
--tertiary-color: #4B607C;
--on-tertiary-color: #FFFFFF;
--tertiary-container-color: #D3E4FF;
--on-tertiary-container-color: #041C35;
--error-color: #BA1A1A;
--on-error-color: #FFFFFF;
--error-container-color: #FFDAD6;
--on-error-container-color: #410002;
--surface-color: #FAFDFC;
--on-surface-color: #191C1C;
--surface-variant-color: #DAE5E3;
--on-surface-variant-color: #3F4948;
--outline-color: #6F7978;
--background-color: #FAFDFC;
--on-background-color: #191C1C;
--elevation-level-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
--elevation-level-2: 0px 2px 6px 2px rgba(0, 0, 0, 0.15);
--elevation-level-3: 0px 4px 8px 3px rgba(0, 0, 0, 0.15);
--surface-tint-color: #006A6A;
}
<<<<<<< HEAD
<!-- Roboto Font (recommended for Material Design) --> <!-- Roboto Font (recommended for Material Design) -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@ -569,5 +605,856 @@
setTimeout(initApp, 200); setTimeout(initApp, 200);
}); });
</script> </script>
=======
.theme-dark {
/* Material 3 (You/Expressive) Colors - Dark Theme */
--primary-color: #4CDADA;
--on-primary-color: #003737;
--primary-container-color: #004F4F;
--on-primary-container-color: #6FF7F7;
--secondary-color: #B0CCCC;
--on-secondary-color: #1B3534;
--secondary-container-color: #324B4B;
--on-secondary-container-color: #CCE8E7;
--tertiary-color: #B4C8E9;
--on-tertiary-color: #1C314B;
--tertiary-container-color: #334863;
--on-tertiary-container-color: #D3E4FF;
--error-color: #FFB4AB;
--on-error-color: #690005;
--error-container-color: #93000A;
--on-error-container-color: #FFDAD6;
--surface-color: #191C1C;
--on-surface-color: #E0E3E2;
--surface-variant-color: #3F4948;
--on-surface-variant-color: #BEC9C7;
--outline-color: #899392;
--background-color: #191C1C;
--on-background-color: #E0E3E2;
--elevation-level-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.35);
--elevation-level-2: 0px 2px 6px 2px rgba(0, 0, 0, 0.35);
--elevation-level-3: 0px 4px 8px 3px rgba(0, 0, 0, 0.35);
--surface-tint-color: #4CDADA;
}
.theme-sepia {
/* Material 3 (You/Expressive) Colors - Sepia Theme */
--primary-color: #8B5000;
--on-primary-color: #FFFFFF;
--primary-container-color: #FFDDB7;
--on-primary-container-color: #2C1700;
--secondary-color: #735A41;
--on-secondary-color: #FFFFFF;
--secondary-container-color: #FFDDBD;
--on-secondary-container-color: #291805;
--tertiary-color: #5B6237;
--on-tertiary-color: #FFFFFF;
--tertiary-container-color: #DFE8B2;
--on-tertiary-container-color: #181E00;
--error-color: #BA1A1A;
--on-error-color: #FFFFFF;
--error-container-color: #FFDAD6;
--on-error-container-color: #410002;
--surface-color: #FFFBFF;
--on-surface-color: #201B16;
--surface-variant-color: #F0E0CF;
--on-surface-variant-color: #4E4539;
--outline-color: #7F7667;
--background-color: #FFF8F3;
--on-background-color: #201B16;
--elevation-level-1: 0px 1px 3px 1px rgba(139, 80, 0, 0.15);
--elevation-level-2: 0px 2px 6px 2px rgba(139, 80, 0, 0.15);
--elevation-level-3: 0px 4px 8px 3px rgba(139, 80, 0, 0.15);
--surface-tint-color: #8B5000;
}
body {
font-family: 'Google Sans', 'Roboto', sans-serif;
margin: 0;
padding: 0;
background-color: var(--background-color);
color: var(--on-background-color);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.header {
background-color: var(--primary-color);
color: var(--on-primary-color);
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--elevation-level-2);
z-index: 10;
}
.app-title {
font-size: 1.5rem;
font-weight: 500;
margin: 0;
font-family: 'Google Sans', sans-serif;
}
.main-container {
display: flex;
flex: 1;
overflow: hidden;
}
.sidebar {
width: 280px;
background-color: var(--surface-color);
border-right: 1px solid var(--outline-color);
display: flex;
flex-direction: column;
overflow-y: auto;
box-shadow: var(--elevation-level-1);
}
.sidebar-header {
padding: 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--outline-color);
background-color: var(--surface-variant-color);
color: var(--on-surface-variant-color);
}
.note-list {
list-style: none;
padding: 0;
margin: 0;
flex-grow: 1;
overflow-y: auto;
}
.note-item {
padding: 16px;
cursor: pointer;
border-bottom: 1px solid var(--outline-color);
transition: background-color 0.2s, transform 0.1s;
border-radius: 12px;
margin: 8px;
}
.note-item:hover {
background-color: var(--surface-variant-color);
transform: translateY(-2px);
}
.note-item.active {
background-color: var(--primary-container-color);
color: var(--on-primary-container-color);
box-shadow: var(--elevation-level-1);
}
.note-item-title {
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.note-item-preview {
font-size: 0.85rem;
opacity: 0.75;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.editor-container {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.toolbar {
background-color: var(--surface-color);
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding: 8px 16px;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.editor {
flex: 1;
padding: 16px;
overflow-y: auto;
outline: none;
font-size: 1rem;
line-height: 1.6;
border: none;
background-color: var(--surface-color);
color: var(--on-surface-color);
resize: none;
}
.button {
background-color: var(--primary-container-color);
color: var(--on-primary-container-color);
border: none;
border-radius: 20px;
padding: 10px 16px;
font-family: 'Google Sans', sans-serif;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s ease;
box-shadow: var(--elevation-level-1);
}
.button:hover {
background-color: var(--primary-container-color);
opacity: 0.9;
box-shadow: var(--elevation-level-2);
}
.button-icon {
font-size: 1.25rem;
}
.theme-selector {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
padding: 8px;
background-color: var(--surface-variant-color);
border-radius: 24px;
}
.theme-button {
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: transform 0.2s, border-color 0.2s;
}
.theme-button.active {
border-color: var(--primary-color);
transform: scale(1.2);
}
.theme-light {
background-color: #FFFBFE;
}
.theme-dark {
background-color: #1C1B1F;
}
.theme-sepia {
background-color: #F5F5DC;
}
.status-bar {
padding: 8px 16px;
font-size: 0.75rem;
background-color: var(--surface-color);
border-top: 1px solid rgba(0, 0, 0, 0.12);
display: flex;
justify-content: space-between;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: var(--surface-color);
padding: 32px;
border-radius: 28px;
width: 100%;
max-width: 400px;
box-shadow: var(--elevation-level-3);
}
.modal-title {
font-size: 1.5rem;
font-weight: 500;
margin-top: 0;
margin-bottom: 24px;
color: var(--on-surface-color);
font-family: 'Google Sans', sans-serif;
}
.form-field {
margin-bottom: 24px;
}
.input-field {
width: 100%;
padding: 16px;
font-family: 'Google Sans', 'Roboto', sans-serif;
font-size: 1rem;
border: 2px solid var(--outline-color);
border-radius: 16px;
background-color: var(--surface-color);
color: var(--on-surface-color);
transition: border-color 0.2s;
}
.input-field:focus {
border-color: var(--primary-color);
outline: none;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.info-message {
background-color: var(--container-color);
color: var(--on-container-color);
padding: 8px 16px;
margin: 8px 0;
border-radius: 4px;
display: none;
}
@media (max-width: 768px) {
.sidebar {
position: absolute;
left: -250px;
height: 100%;
transition: left 0.3s ease;
z-index: 100;
}
.sidebar.open {
left: 0;
}
.toggle-sidebar {
display: block;
}
}
.hidden {
display: none;
}
.material-symbols-outlined {
font-size: 24px;
}
</style>
</head>
<body>
<div class="header">
<div class="left-section">
<button id="toggleSidebar" class="button">
<span class="material-symbols-rounded">menu</span>
</button>
<h1 class="app-title">MNotes</h1>
</div>
<div class="theme-selector">
<button class="theme-button theme-light active" data-theme="light" title="Light theme"></button>
<button class="theme-button theme-dark" data-theme="dark" title="Dark theme"></button>
<button class="theme-button theme-sepia" data-theme="sepia" title="Sepia theme"></button>
</div>
</div>
<div class="main-container">
<div class="sidebar" id="sidebar">
<div class="sidebar-header">
<h2>Notes</h2>
<button id="newNoteBtn" class="button">
<span class="material-symbols-rounded">add</span>
</button>
</div>
<ul class="note-list" id="noteList">
<!-- Note items will be added here -->
</ul>
</div>
<div class="editor-container">
<div class="toolbar">
<button id="saveNote" class="button">
<span class="material-symbols-rounded">save</span>
Save
</button>
<button id="downloadNote" class="button">
<span class="material-symbols-rounded">download</span>
Download
</button>
<button id="githubSave" class="button">
<span class="material-symbols-rounded">cloud_upload</span>
Save to GitHub
</button>
<button id="deleteNote" class="button">
<span class="material-symbols-rounded">delete</span>
Delete
</button>
</div>
<textarea id="editor" class="editor" placeholder="Start typing your note here..."></textarea>
<div class="status-bar">
<span id="wordCount">0 words</span>
<span id="lastSaved">Not saved yet</span>
</div>
</div>
</div>
<!-- GitHub Auth Modal -->
<div class="modal" id="githubModal">
<div class="modal-content">
<h2 class="modal-title">GitHub Authentication</h2>
<div class="form-field">
<input type="text" id="githubUsername" class="input-field" placeholder="GitHub Username">
</div>
<div class="form-field">
<input type="password" id="githubToken" class="input-field" placeholder="Personal Access Token">
</div>
<div class="form-field">
<input type="text" id="githubRepo" class="input-field" placeholder="Repository Name">
</div>
<div class="info-message" id="githubMessage"></div>
<div class="modal-actions">
<button id="cancelGithubAuth" class="button">Cancel</button>
<button id="confirmGithubAuth" class="button">Connect</button>
</div>
</div>
</div>
<!-- New Note Modal -->
<div class="modal" id="newNoteModal">
<div class="modal-content">
<h2 class="modal-title">Create New Note</h2>
<div class="form-field">
<input type="text" id="newNoteTitle" class="input-field" placeholder="Note Title">
</div>
<div class="modal-actions">
<button id="cancelNewNote" class="button">Cancel</button>
<button id="confirmNewNote" class="button">Create</button>
</div>
</div>
</div>
<script>
// Main app logic
document.addEventListener('DOMContentLoaded', function() {
// State variables
let notes = [];
let currentNoteId = null;
let githubAuth = {
username: '',
token: '',
repo: ''
};
// DOM elements
const editor = document.getElementById('editor');
const noteList = document.getElementById('noteList');
const wordCount = document.getElementById('wordCount');
const lastSaved = document.getElementById('lastSaved');
const toggleSidebarBtn = document.getElementById('toggleSidebar');
const sidebar = document.getElementById('sidebar');
const newNoteBtn = document.getElementById('newNoteBtn');
const saveNoteBtn = document.getElementById('saveNote');
const downloadNoteBtn = document.getElementById('downloadNote');
const githubSaveBtn = document.getElementById('githubSave');
const deleteNoteBtn = document.getElementById('deleteNote');
const themeButtons = document.querySelectorAll('.theme-button');
// Modals
const githubModal = document.getElementById('githubModal');
const githubUsername = document.getElementById('githubUsername');
const githubToken = document.getElementById('githubToken');
const githubRepo = document.getElementById('githubRepo');
const githubMessage = document.getElementById('githubMessage');
const confirmGithubAuth = document.getElementById('confirmGithubAuth');
const cancelGithubAuth = document.getElementById('cancelGithubAuth');
const newNoteModal = document.getElementById('newNoteModal');
const newNoteTitle = document.getElementById('newNoteTitle');
const confirmNewNote = document.getElementById('confirmNewNote');
const cancelNewNote = document.getElementById('cancelNewNote');
// Load data from localStorage
function loadFromLocalStorage() {
// Load notes
const savedNotes = localStorage.getItem('notes');
if (savedNotes) {
notes = JSON.parse(savedNotes);
renderNoteList();
}
// Load theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
}
// Load GitHub auth
const savedGithubAuth = localStorage.getItem('githubAuth');
if (savedGithubAuth) {
githubAuth = JSON.parse(savedGithubAuth);
}
}
// Create a new note
function createNewNote(title) {
const newNote = {
id: Date.now().toString(),
title: title || 'Untitled Note',
content: '',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
notes.push(newNote);
saveNotesToLocalStorage();
renderNoteList();
openNote(newNote.id);
}
// Open a note
function openNote(noteId) {
const note = notes.find(n => n.id === noteId);
if (!note) return;
currentNoteId = noteId;
editor.value = note.content;
updateWordCount();
updateLastSaved(note.updatedAt);
// Update active state in note list
const noteItems = document.querySelectorAll('.note-item');
noteItems.forEach(item => {
if (item.dataset.id === noteId) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
// Save the current note
function saveCurrentNote() {
if (!currentNoteId) return;
const noteIndex = notes.findIndex(n => n.id === currentNoteId);
if (noteIndex === -1) return;
notes[noteIndex].content = editor.value;
notes[noteIndex].updatedAt = new Date().toISOString();
saveNotesToLocalStorage();
updateLastSaved(notes[noteIndex].updatedAt);
renderNoteList();
}
// Delete the current note
function deleteCurrentNote() {
if (!currentNoteId) return;
const confirmDelete = confirm('Are you sure you want to delete this note?');
if (!confirmDelete) return;
notes = notes.filter(n => n.id !== currentNoteId);
saveNotesToLocalStorage();
renderNoteList();
if (notes.length > 0) {
openNote(notes[0].id);
} else {
currentNoteId = null;
editor.value = '';
updateWordCount();
updateLastSaved(null);
}
}
// Download the current note as Markdown
function downloadNoteAsMarkdown() {
if (!currentNoteId) return;
const note = notes.find(n => n.id === currentNoteId);
if (!note) return;
const filename = `${note.title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.md`;
const content = note.content;
const blob = new Blob([content], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Save to GitHub
async function saveToGithub() {
if (!currentNoteId || !githubAuth.username || !githubAuth.token || !githubAuth.repo) {
showGithubModal();
return;
}
const note = notes.find(n => n.id === currentNoteId);
if (!note) return;
const filename = `notes/${note.title.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.md`;
const content = note.content;
try {
// First, try to get the file to see if it exists
let sha;
try {
const getResponse = await fetch(`https://api.github.com/repos/${githubAuth.username}/${githubAuth.repo}/contents/${filename}`, {
headers: {
'Authorization': `token ${githubAuth.token}`
}
});
if (getResponse.status === 200) {
const fileData = await getResponse.json();
sha = fileData.sha;
}
} catch (error) {
// File doesn't exist, that's fine
}
// Now create or update the file
const response = await fetch(`https://api.github.com/repos/${githubAuth.username}/${githubAuth.repo}/contents/${filename}`, {
method: 'PUT',
headers: {
'Authorization': `token ${githubAuth.token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: `Update note: ${note.title}`,
content: btoa(unescape(encodeURIComponent(content))),
sha: sha
})
});
if (response.status === 200 || response.status === 201) {
alert('Note saved to GitHub successfully!');
} else {
const error = await response.json();
throw new Error(error.message);
}
} catch (error) {
alert(`Error saving to GitHub: ${error.message}`);
}
}
// Show GitHub modal
function showGithubModal() {
githubUsername.value = githubAuth.username || '';
githubToken.value = githubAuth.token || '';
githubRepo.value = githubAuth.repo || '';
githubModal.style.display = 'flex';
}
// Hide GitHub modal
function hideGithubModal() {
githubModal.style.display = 'none';
}
// Show new note modal
function showNewNoteModal() {
newNoteTitle.value = '';
newNoteModal.style.display = 'flex';
newNoteTitle.focus();
}
// Hide new note modal
function hideNewNoteModal() {
newNoteModal.style.display = 'none';
}
// Save GitHub auth
function saveGithubAuth() {
githubAuth.username = githubUsername.value.trim();
githubAuth.token = githubToken.value.trim();
githubAuth.repo = githubRepo.value.trim();
if (!githubAuth.username || !githubAuth.token || !githubAuth.repo) {
githubMessage.textContent = 'All fields are required';
githubMessage.style.display = 'block';
return;
}
localStorage.setItem('githubAuth', JSON.stringify(githubAuth));
hideGithubModal();
// Try to save the current note to GitHub
if (currentNoteId) {
saveToGithub();
}
}
// Save notes to localStorage
function saveNotesToLocalStorage() {
localStorage.setItem('notes', JSON.stringify(notes));
}
// Render the note list
function renderNoteList() {
noteList.innerHTML = '';
if (notes.length === 0) {
const emptyItem = document.createElement('li');
emptyItem.className = 'note-item';
emptyItem.textContent = 'No notes yet';
noteList.appendChild(emptyItem);
return;
}
// Sort notes by updated date (newest first)
const sortedNotes = [...notes].sort((a, b) => {
return new Date(b.updatedAt) - new Date(a.updatedAt);
});
sortedNotes.forEach(note => {
const noteItem = document.createElement('li');
noteItem.className = 'note-item';
if (note.id === currentNoteId) {
noteItem.classList.add('active');
}
noteItem.dataset.id = note.id;
const titleElement = document.createElement('div');
titleElement.className = 'note-item-title';
titleElement.textContent = note.title;
const previewElement = document.createElement('div');
previewElement.className = 'note-item-preview';
previewElement.textContent = note.content.substring(0, 50) || 'Empty note';
noteItem.appendChild(titleElement);
noteItem.appendChild(previewElement);
noteItem.addEventListener('click', () => {
openNote(note.id);
});
noteList.appendChild(noteItem);
});
}
// Update word count
function updateWordCount() {
const text = editor.value.trim();
const words = text ? text.split(/\s+/).length : 0;
wordCount.textContent = `${words} word${words === 1 ? '' : 's'}`;
}
// Update last saved timestamp
function updateLastSaved(timestamp) {
if (!timestamp) {
lastSaved.textContent = 'Not saved yet';
return;
}
const date = new Date(timestamp);
lastSaved.textContent = `Last saved: ${date.toLocaleString()}`;
}
// Set theme
function setTheme(theme) {
document.body.className = '';
if (theme !== 'light') {
document.body.classList.add(`theme-${theme}`);
}
themeButtons.forEach(button => {
if (button.dataset.theme === theme) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
});
localStorage.setItem('theme', theme);
}
// Toggle sidebar (for mobile)
function toggleSidebar() {
sidebar.classList.toggle('open');
}
// Event listeners
editor.addEventListener('input', () => {
updateWordCount();
});
toggleSidebarBtn.addEventListener('click', toggleSidebar);
saveNoteBtn.addEventListener('click', saveCurrentNote);
downloadNoteBtn.addEventListener('click', downloadNoteAsMarkdown);
githubSaveBtn.addEventListener('click', saveToGithub);
deleteNoteBtn.addEventListener('click', deleteCurrentNote);
newNoteBtn.addEventListener('click', showNewNoteModal);
confirmGithubAuth.addEventListener('click', saveGithubAuth);
cancelGithubAuth.addEventListener('click', hideGithubModal);
confirmNewNote.addEventListener('click', () => {
const title = newNoteTitle.value.trim() || 'Untitled Note';
createNewNote(title);
hideNewNoteModal();
});
cancelNewNote.addEventListener('click', hideNewNoteModal);
// Theme selection
themeButtons.forEach(button => {
button.addEventListener('click', () => {
setTheme(button.dataset.theme);
});
});
// Auto-save timer (every 30 seconds)
setInterval(() => {
if (currentNoteId && editor.value) {
saveCurrentNote();
}
}, 30000);
// Initialize
loadFromLocalStorage();
// Create first note if none exist
if (notes.length === 0) {
createNewNote('Welcome Note');
} else {
openNote(notes[0].id);
}
});
</script>
>>>>>>> c32bf888e7b226dce8ed1e85ec205b9386d99cf6
</body> </body>
</html> </html>

180
style.css
View file

@ -1,180 +0,0 @@
/* Global variables for theming */
:root {
/* Light Theme (Default) */
--app-bg-color: #FEF7FF; /* Material 3 Surface */
--app-text-color: #1D1B20; /* Material 3 On Surface */
--app-surface-color: #FDFCFB; /* Material 3 Surface Container Low */
--app-primary-color: #6750A4; /* Material 3 Primary */
--app-on-primary-color: #FFFFFF;
--app-secondary-color: #625B71; /* Material 3 Secondary */
--app-outline-color: #79747E; /* Material 3 Outline */
--app-note-card-bg: #EADDFF; /* Material 3 Primary Container */
--app-note-card-text: #21005D; /* Material 3 On Primary Container */
--app-note-card-header: var(--app-primary-color); /* Or #1D192B for On Surface */
/* MWC Theme Token Mapping */
--md-sys-color-background: var(--app-bg-color);
--md-sys-color-on-background: var(--app-text-color);
--md-sys-color-surface: var(--app-surface-color);
--md-sys-color-surface-dim: #DED8E1; /* Example, add more as needed */
--md-sys-color-surface-bright: #FEF7FF;
--md-sys-color-surface-container-lowest: #FFFFFF;
--md-sys-color-surface-container-low: #F7F2FA;
--md-sys-color-surface-container: #F3EDF7;
--md-sys-color-surface-container-high: #ECE6F0;
--md-sys-color-surface-container-highest: #E6E0E9; /* Was used for note cards */
--md-sys-color-surface-variant: #E7E0EC;
--md-sys-color-on-surface: var(--app-text-color);
--md-sys-color-on-surface-variant: #49454F;
--md-sys-color-primary: var(--app-primary-color);
--md-sys-color-on-primary: var(--app-on-primary-color);
--md-sys-color-secondary: var(--app-secondary-color);
--md-sys-color-on-secondary: #FFFFFF;
--md-sys-color-tertiary: #7D5260;
--md-sys-color-on-tertiary: #FFFFFF;
--md-sys-color-error: #B3261E;
--md-sys-color-on-error: #FFFFFF;
--md-sys-color-outline: var(--app-outline-color);
--md-sys-color-shadow: #000000;
--md-sys-color-surface-tint-color: var(--app-primary-color); /* Alias for primary */
/* Expressive shape tokens */
--md-sys-shape-corner-extra-small: 4px;
--md-sys-shape-corner-small: 8px;
--md-sys-shape-corner-medium: 12px;
--md-sys-shape-corner-large: 16px; /* Used for cards */
--md-sys-shape-corner-extra-large: 28px; /* Used for dialogs */
--md-sys-shape-corner-full: 9999px; /* For fully rounded elements like FAB */
}
body.dark-theme {
/* Dark Theme Overrides */
--app-bg-color: #141218;
--app-text-color: #E6E0E9;
--app-surface-color: #1C1B1F; /* Material 3 Surface Container Low (Dark) */
--app-primary-color: #D0BCFF;
--app-on-primary-color: #381E72;
--app-secondary-color: #CCC2DC;
--app-outline-color: #938F99;
--app-note-card-bg: #4A4458; /* Material 3 Primary Container (Dark) - example, adjust to preference */
--app-note-card-text: #E8DEF8; /* Material 3 On Primary Container (Dark) */
--app-note-card-header: var(--app-primary-color);
/* MWC Dark Theme Token Mapping (subset, repeat for all relevant tokens) */
--md-sys-color-surface-dim: #141218;
--md-sys-color-surface-bright: #3B383E;
--md-sys-color-surface-container-lowest: #0F0D13;
--md-sys-color-surface-container-low: #1D1B20;
--md-sys-color-surface-container: #211F26;
--md-sys-color-surface-container-high: #2B2930;
--md-sys-color-surface-container-highest: #36343B;
--md-sys-color-on-secondary: #332D41;
--md-sys-color-on-tertiary: #492532;
--md-sys-color-on-error: #601410;
--md-sys-color-surface-tint-color: var(--app-primary-color);
}
/* Apply MWC tokens for body styles */
body {
font-family: 'Roboto', sans-serif; /* Already set in head style */
margin: 0;
background-color: var(--md-sys-color-background);
color: var(--md-sys-color-on-background);
transition: background-color 0.3s, color 0.3s;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 16px;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 16px;
margin-bottom: 20px;
}
header h1 {
margin: 0;
color: var(--md-sys-color-primary);
font-size: 2em; /* More prominent title */
}
#themeToggleBtn md-icon {
color: var(--md-sys-color-secondary); /* Themed icon color */
}
/* Note Card Styling */
.note-card {
margin-bottom: 16px;
padding: 16px;
border-radius: var(--md-sys-shape-corner-large); /* Expressive rounding via token */
background-color: var(--app-note-card-bg);
color: var(--app-note-card-text);
position: relative;
/* box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 2px 8px rgba(0,0,0,0.1); MWC Elevation handles this */
overflow: hidden;
}
.note-card md-elevation { /* This applies the standard MWC shadow */
--md-elevation-level: 1;
}
.note-card h3 {
margin-top: 0;
margin-bottom: 8px;
color: var(--app-note-card-header);
font-size: 1.25em;
font-weight: 500;
}
.note-card p {
margin-bottom: 8px;
white-space: pre-wrap;
font-size: 0.95em;
line-height: 1.5;
}
.note-card-actions {
position: absolute;
top: 8px;
right: 8px;
display: flex;
}
.note-card-actions md-icon-button {
--md-icon-button-icon-color: var(--app-note-card-text);
}
.note-card-actions md-icon-button:hover {
--md-icon-button-icon-color: var(--app-primary-color);
}
/* FAB Styling (MWC theming should mostly handle this) */
md-fab {
position: fixed;
bottom: 24px;
right: 24px;
/* --md-fab-container-shape: var(--md-sys-shape-corner-large); /* Example of shape override if not default */
}
/* Dialog Styling */
md-dialog {
/* MWC sets its own width, can be overridden with --md-dialog-container-min-width etc. */
/* border-radius: var(--md-sys-shape-corner-extra-large); /* MWC default or use --md-dialog-container-shape */
--md-dialog-container-color: var(--md-sys-color-surface-container-high); /* Themed dialog bg */
--md-dialog-headline-color: var(--md-sys-color-on-surface-variant);
--md-dialog-supporting-text-color: var(--md-sys-color-on-surface-variant);
}
md-dialog [slot="headline"] {
font-size: 1.5em;
font-weight: 500;
/* color: var(--md-sys-color-primary); /* Already set by --md-dialog-headline-color or inherited */
}
/* Placeholder text for empty notes list */
#notesList > p {
text-align: center;
color: var(--md-sys-color-outline);
padding: 20px 0;
font-size: 1.1em;
}