commit cd6624be37f2297812deedad57f87450115c33a8 Author: Guilleag01 Date: Sun Aug 6 17:40:50 2023 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..48c3ca4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/dist/ +/target/ +/Cargo.lock diff --git a/.taurignore b/.taurignore new file mode 100644 index 0000000..1ebdc6d --- /dev/null +++ b/.taurignore @@ -0,0 +1,3 @@ +/src +/public +/Cargo.toml \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..24d7cc6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e6d9d21 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "emmet.includeLanguages": { + "rust": "html" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..9d3fd97 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "tasks": [ + { + "label": "cargo tauri dev", + "type": "shell", + "command": "cargo", // note: full path to the cargo + "args": [ + "tauri", + "dev" + ], + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bbb5ffb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "minesweeper-ui" +version = "0.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +yew = { version = "0.20", features = ["csr"] } +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.4" +wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasm-bindgen-futures = "0.4" +web-sys = "0.3" +js-sys = "0.3" +getrandom = { version = "0.2.10", features = ["js"] } +rand = "0.8.5" +wasm-logger = "0.2.0" +log = "0.4.19" + +[workspace] +members = ["src-tauri"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..4eee105 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Tauri + Yew + +This template should help get you started developing with Tauri and Yew. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 0000000..27a1f84 --- /dev/null +++ b/Trunk.toml @@ -0,0 +1,10 @@ +[build] +target = "./index.html" + +[watch] +ignore = ["./src-tauri"] + +[serve] +address = "127.0.0.1" +port = 1420 +open = false diff --git a/index.html b/index.html new file mode 100644 index 0000000..e0423f3 --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + + Tauri + Yew App + + + + diff --git a/public/tauri.svg b/public/tauri.svg new file mode 100644 index 0000000..31b62c9 --- /dev/null +++ b/public/tauri.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/yew.png b/public/yew.png new file mode 100644 index 0000000..e2062e8 Binary files /dev/null and b/public/yew.png differ diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..f4dfb82 --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..755711c --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "minesweeper" +version = "0.0.0" +description = "A Tauri App" +authors = ["you"] +license = "" +repository = "" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tauri-build = { version = "1.4", features = [] } + +[dependencies] +tauri = { version = "1.4", features = ["shell-open"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem +# DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..6be5e50 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..e81bece Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..a437dd5 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..0ca4f27 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..b81f820 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..624c7bf Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..c021d2b Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..6219700 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..f9bc048 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..d5fbfb2 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..63440d7 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..f3f705a Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..4556388 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..12a5bce Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..b3636e4 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..e1cd261 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..523550d --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,15 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command +#[tauri::command] +fn greet(name: &str) -> String { + format!("Hello, {}! You've been greeted from Rust!", name) +} + +fn main() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..c8376ff --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,46 @@ +{ + "build": { + "beforeDevCommand": "trunk serve", + "beforeBuildCommand": "trunk build", + "devPath": "http://localhost:1420", + "distDir": "../dist", + "withGlobalTauri": true + }, + "package": { + "productName": "minesweeper", + "version": "0.0.0" + }, + "tauri": { + "allowlist": { + "all": false, + "shell": { + "all": false, + "open": true + } + }, + "bundle": { + "active": true, + "targets": "all", + "identifier": "com.tauri.dev", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + }, + "security": { + "csp": null + }, + "windows": [ + { + "fullscreen": false, + "resizable": true, + "title": "minesweeper", + "width": 800, + "height": 600 + } + ] + } +} diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..3617af6 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,77 @@ +use serde::{Deserialize, Serialize}; +// use serde_wasm_bindgen::to_value; +use wasm_bindgen::prelude::*; +// use web_sys::EventTarget; +// use wasm_bindgen_futures::spawn_local; +use yew::{prelude::*, html::Scope}; + +use log::info; +use wasm_bindgen::JsValue; + +use minesweeper_ui::components::{button::Button, board::BoardComponent}; + +use minesweeper_ui::minesweeper::{cell::Cell, board::Board, Game}; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "tauri"])] + async fn invoke(cmd: &str, args: JsValue) -> JsValue; +} + +pub enum Msg { + Discover{cell: Cell} +} + +pub struct App { + link: Scope, + game: Game +} + + +// #[function_component(App)] +// pub fn app() -> Html { +// html! { + +// } +// } + +impl Component for App { + type Message = Msg; + + type Properties = (); + + fn create(ctx: &Context) -> Self { + let mut game = Game::new(25, 40, 50); + game.start_board(); + + info!("\n{}", game.get_board().to_string()); + + Self { + link: ctx.link().clone(), + game + } + } + + fn view(&self, ctx: &Context) -> Html { + let b = self.game.get_board().clone(); + + html!{ +
+ +
+ } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::Discover {cell} => { + // info!("Pos (from App): {}", format!("{:?}", cell.get_pos())); + self.game.show(cell.get_pos()); + + // info!("celhid (from App): {}", format!("{:?}", self.game.get_cell(cell.get_pos()).is_hidden())); + info!("\n{}", self.game.get_board().to_string()) + } + } + true + } +} \ No newline at end of file diff --git a/src/components/board.rs b/src/components/board.rs new file mode 100644 index 0000000..58c0872 --- /dev/null +++ b/src/components/board.rs @@ -0,0 +1,77 @@ +use yew::{prelude::*, html::Scope}; + +use crate::minesweeper::{board::Board, cell::Cell}; + +use crate::components::button::Button; + +use log::info; +use wasm_bindgen::JsValue; + +pub struct BoardComponent { + link: Scope, + board: Board, + onsignal: Callback +} + +pub enum Msg { + Discover{ cell: Cell }, +} + +#[derive(Clone, PartialEq, Properties)] +pub struct Props { + pub board: Board, + pub onsignal: Callback, +} + +impl Component for BoardComponent { + type Message = Msg; + + type Properties = Props; + + fn create(ctx: &Context) -> Self { + Self { + link: ctx.link().clone(), + board: ctx.props().board.clone(), + onsignal: ctx.props().onsignal.clone() + } + } + + fn view(&self, ctx: &Context) -> Html { + + let b = ctx.props().board.get_board().to_owned(); + + html! { +
+ {b.into_iter().map( |row| { + html! { + <> + {row.into_iter().map(|c| { + html! { +
+ } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::Discover {cell} => { + // info!("Pos (from board): {}", format!("{:?}", cell.get_pos())); + self.onsignal.emit(cell) + } + } + true + } + + fn changed(&mut self, ctx: &Context, _old_props: &Self::Properties) -> bool { + self.board = ctx.props().board.clone(); + self.onsignal = ctx.props().onsignal.clone(); + true + } + +} \ No newline at end of file diff --git a/src/components/button.rs b/src/components/button.rs new file mode 100644 index 0000000..57c5a4a --- /dev/null +++ b/src/components/button.rs @@ -0,0 +1,61 @@ + +use yew::{prelude::*, html::Scope}; + +use crate::minesweeper::cell::Cell; + +use log::info; +use wasm_bindgen::JsValue; + +pub struct Button { + link: Scope, + cell: Cell, + onsignal: Callback, +} + +pub enum Msg { + Clicked, +} + +#[derive(Clone, PartialEq, Properties)] +pub struct Props { + pub cell: Cell, + pub onsignal: Callback, +} + +impl Component for Button { + type Message = Msg; + type Properties = Props; + + fn create(ctx: &Context) -> Self { + Self { + link: ctx.link().clone(), + cell: ctx.props().cell.clone(), + onsignal: ctx.props().onsignal.clone() + } + } + + fn view(&self, ctx: &Context) -> Html { + + html!{ + + } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::Clicked => { + // info!("hid (from cell): {}", format!("{:?}", self.cell.is_hidden())); + self.onsignal.emit(self.cell) + } + } + true + } + + fn changed(&mut self, ctx: &Context, _old_props: &Self::Properties) -> bool { + self.cell = ctx.props().cell.clone(); + self.onsignal = ctx.props().onsignal.clone(); + true + } +} \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs new file mode 100644 index 0000000..920c3bd --- /dev/null +++ b/src/components/mod.rs @@ -0,0 +1,2 @@ +pub mod button; +pub mod board; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7e5b58c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod minesweeper; +pub mod components; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..344345f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +mod app; + +use app::App; + +fn main() { + wasm_logger::init(wasm_logger::Config::default()); + yew::Renderer::::new().render(); +} diff --git a/src/minesweeper/board.rs b/src/minesweeper/board.rs new file mode 100644 index 0000000..0bbd0b5 --- /dev/null +++ b/src/minesweeper/board.rs @@ -0,0 +1,107 @@ + +use super::cell::Cell; + +#[derive(Clone, PartialEq)] +pub struct Board { + board: Vec>, + height: usize, + width: usize, + num_mines: usize +} + +impl Board { + pub fn new(height: usize, width: usize, num_mines: usize) -> Self { + let mut t: Vec> = Vec::new(); + for i in 0..height { + t.push(Vec::new()); + for j in 0..width { + t[i].push(Cell::new((i, j))) + } + } + + return Self { + board: t, + height, + width, + num_mines + }; + } + + pub fn calculate_value(&self, pos: (usize, usize)) -> usize { + let mut value: usize = 0; + for i in -1..=1 { + for j in -1..=1 { + let new_pos: (isize, isize) = ((pos.0 as isize) + i, (pos.1 as isize) + j); + if new_pos.0 >= 0 && new_pos.0 < (self.height as isize) && new_pos.1 >= 0 && new_pos.1 < (self.width as isize) { + value += self.is_mine((new_pos.0 as usize, new_pos.1 as usize)) as usize; + } + } + } + return value; + } + + pub fn get_board(&self) -> &Vec> { + return &self.board; + } + + pub fn set_mine(&mut self, pos: (usize, usize), value: bool) { + self.board[pos.0][pos.1].set_mine(value); + } + + pub fn get_height(&self) -> usize { + return self.height; + } + + pub fn get_width(&self) -> usize { + return self.width; + } + + pub fn get_num_mines(&self) -> usize { + return self.num_mines; + } + + pub fn is_mine(&self, pos: (usize, usize)) -> bool { + return self.board[pos.0][pos.1].is_mine(); + } + + pub fn get_value(&self, pos: (usize, usize)) -> usize { + return self.board[pos.0][pos.1].get_value(); + } + + pub fn get_cell(&self, pos: (usize, usize)) -> Cell { + return self.board[pos.0][pos.1]; + } + + pub fn set_value(&mut self, pos: (usize, usize), new_value: usize) { + return self.board[pos.0][pos.1].set_value(new_value) + } + + pub fn show_cell(&mut self, pos: (usize, usize)) { + self.board[pos.0][pos.1].show(); + } + +} + +impl ToString for Board { + fn to_string(&self) -> String { + let mut result = String::new(); + result.push_str(" "); + for i in 0..self.width { + // result.push_str(" "); + result.push_str(i.to_string().as_str()); + } + result.push_str("\n"); + + for i in 0..self.height { + result.push_str(i.to_string().as_str()); + result.push_str(" "); + + for j in 0..self.width { + result.push_str(self.get_cell((i, j)).to_string().as_str()); + } + result.push_str("\n"); + } + + return result; + } +} \ No newline at end of file diff --git a/src/minesweeper/cell.rs b/src/minesweeper/cell.rs new file mode 100644 index 0000000..2a893ef --- /dev/null +++ b/src/minesweeper/cell.rs @@ -0,0 +1,72 @@ +#[derive(Clone, Copy, PartialEq)] +pub struct Cell { + pos: (usize, usize), // (height, width) + mine: bool, + value: usize, + hidden: bool, + flagged: bool +} + +impl Cell { + pub fn new(pos: (usize, usize)) -> Self { + return Self { + pos, + mine: false, + value: 0, + hidden: true, + flagged: false + }; + } + + pub fn get_pos(&self) -> (usize, usize) { + return self.pos; + } + + pub fn is_mine(&self) -> bool { + return self.mine; + } + + pub fn set_mine(&mut self, new_mine: bool) { + self.mine = new_mine; + } + + pub fn get_value(&self) -> usize { + return self.value; + } + + pub fn set_value(&mut self, new_value: usize) { + self.value = new_value; + } + + pub fn show(&mut self) { + self.hidden = false; + } + + pub fn is_hidden(&self) -> bool { + return self.hidden; + } + + pub fn is_flagged(&self) -> bool { + return self.flagged; + } + + pub fn set_flag(&mut self, new_flag: bool) { + self.flagged = new_flag; + } + +} + +impl ToString for Cell { + fn to_string(&self) -> String { + if self.hidden { + return " ".to_string(); + } + if self.is_mine() { + return "*".to_string(); + } + if self.value == 0 { + return " ".to_string(); + } + return self.value.to_string(); + } +} \ No newline at end of file diff --git a/src/minesweeper/mod.rs b/src/minesweeper/mod.rs new file mode 100644 index 0000000..6e24b15 --- /dev/null +++ b/src/minesweeper/mod.rs @@ -0,0 +1,74 @@ +pub mod cell; +pub mod board; + +use board::Board; +use cell::Cell; +use rand::Rng; +// use getrandom::getrandom; + +pub struct Game { + board: Board, +} + +impl Game { + pub fn new(height: usize, width: usize, num_mines: usize) -> Self { + Self { + board: Board::new(height, width, num_mines), + } + } + + pub fn start_board(&mut self){ + // TODO: make a better implementation + let mut added_mines = 0; + + let mut rng = rand::thread_rng(); + + while added_mines < self.board.get_num_mines() { + let pos = (rng.gen_range(0..self.board.get_height()), rng.gen_range(0..self.board.get_width())); + if !self.board.is_mine(pos) { + self.board.set_mine(pos, true); + added_mines += 1; + } + } + + // Set values + for i in 0..self.board.get_height() { + for j in 0..self.board.get_width() { + self.board.set_value((i, j), self.board.calculate_value((i, j))) + } + } + } + + pub fn show(&mut self, pos: (usize, usize)) { + self.board.show_cell(pos); + if self.board.get_value(pos) == 0 { + for i in -1..=1 { + for j in -1..=1 { + if pos.0 as isize + i >= 0 && + pos.0 as isize + i < self.get_height() as isize && + pos.1 as isize + j >= 0 && + pos.1 as isize + j < self.get_width() as isize && + self.board.get_cell(((pos.0 as isize + i) as usize , (pos.1 as isize + j) as usize)).is_hidden() { + self.show(((pos.0 as isize + i) as usize , (pos.1 as isize + j) as usize)) + } + } + } + } + } + + pub fn get_height(&self) -> usize { + return self.board.get_height(); + } + + pub fn get_width(&self) -> usize { + return self.board.get_width(); + } + + pub fn get_board(&self) -> &Board { + return &self.board; + } + + pub fn get_cell(&self,pos: (usize, usize)) -> Cell { + self.board.get_cell(pos) + } +} \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..0a633a9 --- /dev/null +++ b/styles.css @@ -0,0 +1,141 @@ +:root { + font-family: Courier , Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color: #0f0f0f; + background-color: #f6f6f6; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +.board{ + text-align: center; +} + +.container { + margin: 0; + padding: 10vh; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: 0.75s; +} + +.logo.tauri:hover { + filter: drop-shadow(0 0 2em #24c8db); +} + +.row { + display: flex; + justify-content: center; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +h1 { + text-align: center; +} + +button { + cursor: pointer; +} + +.button-hidden { + vertical-align: middle; + text-align: center; + border-radius: 8px; + border: 1px solid transparent; + /* padding: 16px; */ + /* font-size: 1em; */ + font-weight: 500; + font-family: inherit; + color: #ffffff; + background-color: #0f0f0f98; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); + height: 50px; + width: 50px; + margin: 2px; + -webkit-transition: background-color 0.5s; + +} + +.button-shown { + vertical-align: middle; + text-align: center; + border-radius: 8px; + border: 1px solid transparent; + /* padding: 16px; */ + font-size: 20px; + font-weight: 500; + font-family: inherit; + color: #ffffff; + background-color: #2f2f2f; + /* transition: border-color 0.25s; */ + /* box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); */ + height: 50px; + width: 50px; + margin: 2px; +} + + + +.button-hidden:hover { + background-color: #333333; + -webkit-transition: background-color 0.5s; +} + + +button:active { + border-color: #396cd8; + background-color: #e8e8e8; +} + +input, +button { + outline: none; +} + +#greet-input { + margin-right: 5px; +} + +@media (prefers-color-scheme: dark) { + :root { + color: #f6f6f6; + background-color: #2f2f2f; + } + + a:hover { + color: #24c8db; + } + + input, + button { + color: #ffffff; + background-color: #0f0f0f98; + } + button:active { + background-color: #0f0f0f69; + } +}