Refactor the board game
parent
8387179bd0
commit
d521d69eac
File diff suppressed because it is too large
Load Diff
|
@ -13,4 +13,5 @@ log = "0.4.20"
|
|||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-logger = "0.2.0"
|
||||
web-sys = "0.3.64"
|
||||
yew = { version = "0.20.0", features = ["csr"] }
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Yew App</title>
|
||||
<link rel="stylesheet" href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
|
||||
<link data-trunk rel="css" href="static/css/styles.css" />
|
||||
<link data-trunk rel="css" href="static/css/normalize.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hola mundo</h1>
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
use crate::consts::{AttemptField, Status};
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AttemptRowProps {
|
||||
pub answer: AttrValue,
|
||||
pub text: AttrValue,
|
||||
pub attempt: Vec<AttemptField>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn AttemptRow(props: &AttemptRowProps) -> Html {
|
||||
let AttemptRowProps { answer, text } = props;
|
||||
let AttemptRowProps { attempt } = props;
|
||||
|
||||
html! {
|
||||
{ for answer.chars().zip(text.chars()).map(|(ans, att)| html! { <p class={
|
||||
if ans == att { "correct" } else if answer.contains(att) { "almost" } else { "missed" }
|
||||
} >{ att }</p> }) }
|
||||
{ for attempt.iter().map(|att| html_nested! {
|
||||
<p
|
||||
class={
|
||||
match att.status {
|
||||
Status::Found => "correct",
|
||||
Status::Almost => "almost",
|
||||
Status::NotFound => "missed",
|
||||
}
|
||||
}
|
||||
>{ att.char_field }</p>
|
||||
})}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
use yew::prelude::*;
|
||||
use crate::components::{EmptyRow, CurrentRow, AttemptRow};
|
||||
use crate::consts::{Attempts, MAX_ATTEMPTS};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct BoardProps {
|
||||
pub attempts: Attempts,
|
||||
pub attempt_index: usize,
|
||||
pub current_attempt: AttrValue,
|
||||
pub onkeypress: Callback<KeyboardEvent>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn Board(props: &BoardProps) -> Html {
|
||||
let BoardProps {
|
||||
attempts,
|
||||
attempt_index,
|
||||
current_attempt,
|
||||
onkeypress
|
||||
} = props;
|
||||
|
||||
html! {
|
||||
<div class="board" onkeyup={onkeypress} tabindex="0">
|
||||
{
|
||||
for (0..MAX_ATTEMPTS).map(|i| {
|
||||
if i < *attempt_index {
|
||||
html! { <AttemptRow attempt={ attempts.fields[i].clone() } /> }
|
||||
} else if i == *attempt_index {
|
||||
html! { <CurrentRow text={ current_attempt } /> }
|
||||
} else {
|
||||
html! { <EmptyRow /> }
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use crate::consts::{Key, KeyboardKeyType, VirtualKey};
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct KeyboardProps {
|
||||
pub onclick: Callback<Key>,
|
||||
pub keyboard: UseStateHandle<Vec<VirtualKey>>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn Keyboard(props: &KeyboardProps) -> Html {
|
||||
let handle_click = |k: Key| {
|
||||
let onclick = props.onclick.clone();
|
||||
|
||||
Callback::from(move |_| {
|
||||
onclick.emit(k);
|
||||
}).clone()
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class="keyboard"> {
|
||||
for (props.keyboard).iter().map(|vk: &VirtualKey| {
|
||||
match vk.key {
|
||||
KeyboardKeyType::CharKey(c) => html! { <button onclick={ handle_click(Key::CharKey(c)) }> { c } </button> },
|
||||
KeyboardKeyType::Backspace => html! { <button onclick={ handle_click(Key::Backspace) } class="key-big"><i class="las la-undo"></i></button> },
|
||||
KeyboardKeyType::Enter => html! { <button onclick={ handle_click(Key::Enter) } class="key-big"> { "Enviar" } </button> }
|
||||
}
|
||||
})
|
||||
} </div>
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
pub use crate::components::attempt_row::AttemptRow;
|
||||
pub use crate::components::current_row::CurrentRow;
|
||||
pub use crate::components::empty_row::EmptyRow;
|
||||
pub use crate::components::keyboard::Keyboard;
|
||||
pub use crate::components::result_board::ResultBoard;
|
||||
pub use crate::components::board::Board;
|
||||
|
||||
mod board;
|
||||
mod attempt_row;
|
||||
mod current_row;
|
||||
mod empty_row;
|
||||
mod keyboard;
|
||||
mod result_board;
|
||||
|
|
|
@ -1,33 +1,31 @@
|
|||
use crate::consts::GameResult;
|
||||
use yew::prelude::*;
|
||||
use crate::consts::{Attempts, Status};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ResultBoardProps {
|
||||
pub answer: AttrValue,
|
||||
pub attempts: Vec<String>,
|
||||
pub attempts: Attempts,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn ResultBoard(props: &ResultBoardProps) -> Html {
|
||||
let ResultBoardProps { answer, attempts } = props;
|
||||
let answer_chars = answer.chars();
|
||||
|
||||
let mut result = String::default();
|
||||
let ResultBoardProps {
|
||||
attempts,
|
||||
} = props;
|
||||
|
||||
html! {
|
||||
<div class="result-board"> {
|
||||
for attempts.iter().map(|attempt| {
|
||||
html! {
|
||||
<> {
|
||||
for answer.chars().zip(attempt.chars()).map(| (ans, att)| {
|
||||
if ans == att {
|
||||
html! { <span> { "🟩" } </span> }
|
||||
} else if answer.contains(att) {
|
||||
html! { <span>{ "🟨" }</span> }
|
||||
} else {
|
||||
html! { <span>{ "⬜" }</span> }
|
||||
for attempts.fields.iter().map(|attempt| {
|
||||
html_nested! {
|
||||
<p class={ "result-board" }> {
|
||||
for attempt.iter().map(| att| {
|
||||
match att.status {
|
||||
Status::Found => html_nested! { <> { "🟩" } </> },
|
||||
Status::Almost => html_nested! { <span>{ "🟨" }</span> },
|
||||
Status::NotFound => html_nested! { <span>{ "⬜" }</span> }
|
||||
}
|
||||
})
|
||||
} </>
|
||||
} </p>
|
||||
}
|
||||
})
|
||||
} </div>
|
||||
|
|
|
@ -1,10 +1,66 @@
|
|||
pub const MAX_ATTEMPTS: usize = 6;
|
||||
use yew::html::IntoPropValue;
|
||||
|
||||
pub enum Key {
|
||||
pub const MAX_ATTEMPTS: usize = 6;
|
||||
pub const WORD_LEN: usize = 5;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum KeyboardKeyType {
|
||||
Enter,
|
||||
// 13
|
||||
Backspace,
|
||||
CharKey(char),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Status {
|
||||
NotFound,
|
||||
Found,
|
||||
Almost,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct AttemptField {
|
||||
pub char_field: char,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Attempts {
|
||||
pub fields: Vec<Vec<AttemptField>>,
|
||||
}
|
||||
|
||||
impl Attempts {
|
||||
pub fn new() -> Self {
|
||||
Attempts {
|
||||
fields: vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct VirtualKey {
|
||||
pub key: KeyboardKeyType,
|
||||
pub status: Option<Status>,
|
||||
}
|
||||
|
||||
impl VirtualKey {
|
||||
pub fn from_charkey(charkey: char) -> Self {
|
||||
VirtualKey {
|
||||
key: KeyboardKeyType::CharKey(charkey),
|
||||
status: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_key(key: KeyboardKeyType) -> Self {
|
||||
VirtualKey { key, status: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Key {
|
||||
// 13
|
||||
Enter,
|
||||
// 8
|
||||
Backspace,
|
||||
CharKey(char),
|
||||
Ignored,
|
||||
}
|
||||
|
@ -21,7 +77,8 @@ impl From<u32> for Key {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum Result {
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum GameResult {
|
||||
Win,
|
||||
Fail,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
pub use use_board::use_board;
|
||||
pub use use_board::UseBoardHandle;
|
||||
|
||||
mod use_board;
|
|
@ -0,0 +1,114 @@
|
|||
use crate::consts::{AttemptField, Attempts, GameResult, Key, VirtualKey, MAX_ATTEMPTS};
|
||||
use crate::services::words::{get_word_of_the_day, WORDS};
|
||||
use crate::utils::{evaluate_status, new_empty_virtual_keyboard};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use yew::prelude::*;
|
||||
|
||||
type AttemptRow = Attempts;
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct UseBoardHandle {
|
||||
pub current_attempt: UseStateHandle<String>,
|
||||
pub attempts: UseStateHandle<AttemptRow>,
|
||||
pub virtual_keyboard: UseStateHandle<Vec<VirtualKey>>,
|
||||
pub result: UseStateHandle<Option<GameResult>>,
|
||||
pub attempt_index: Rc<RefCell<usize>>,
|
||||
answer: Rc<String>,
|
||||
}
|
||||
|
||||
impl UseBoardHandle {
|
||||
pub fn send_key(&self, k: Key) {
|
||||
let current_attempt_len = self.current_attempt.chars().count();
|
||||
|
||||
if self.result.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
match k {
|
||||
Key::CharKey(c) => {
|
||||
if current_attempt_len >= 5 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_attempt = (*self.current_attempt).clone();
|
||||
new_attempt.push(c);
|
||||
self.current_attempt.set(new_attempt);
|
||||
}
|
||||
Key::Backspace => {
|
||||
if current_attempt_len == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_attempt = (*self.current_attempt).clone();
|
||||
// This unwrap is safe because the potential failura it's already covered
|
||||
new_attempt.pop().unwrap();
|
||||
self.current_attempt.set(new_attempt);
|
||||
}
|
||||
Key::Enter => {
|
||||
if current_attempt_len < 5 {
|
||||
return;
|
||||
}
|
||||
if !WORDS.contains(&*self.current_attempt) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_attempts = (*self.attempts).clone();
|
||||
let new_attempt_row = (*self.current_attempt)
|
||||
.clone()
|
||||
.chars()
|
||||
.zip((*self.answer).chars())
|
||||
.map(|(att, ans)| {
|
||||
let status = evaluate_status(ans, att, (*self.answer).clone());
|
||||
AttemptField {
|
||||
char_field: att,
|
||||
status,
|
||||
}
|
||||
}).collect();
|
||||
new_attempts.fields.push(new_attempt_row);
|
||||
|
||||
self.attempts.set(new_attempts);
|
||||
*self.attempt_index.borrow_mut() += 1;
|
||||
|
||||
if (*self.current_attempt) == (*self.answer) {
|
||||
self.result.set(Some(GameResult::Win));
|
||||
} else if *self.attempt_index.borrow() == MAX_ATTEMPTS {
|
||||
self.result.set(Some(GameResult::Fail));
|
||||
}
|
||||
|
||||
self.current_attempt.set(String::default());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hook]
|
||||
pub fn use_board() -> UseBoardHandle {
|
||||
let current_attempt = use_state(|| "".to_string());
|
||||
let attempts: UseStateHandle<Attempts> = use_state(|| Attempts::new());
|
||||
let attempt_index = use_mut_ref(|| 0usize);
|
||||
// let answer = use_memo(|_| get_word_of_the_day(), None::<()>);
|
||||
let answer = use_memo(|_| "ABEJA".to_owned(), None::<()>);
|
||||
let virtual_keyboard = use_state(|| new_empty_virtual_keyboard().into());
|
||||
let result = use_state(|| None::<GameResult>);
|
||||
|
||||
let send_key = {
|
||||
let current_attempt = current_attempt.clone();
|
||||
let current_attempt_len = current_attempt.chars().count();
|
||||
let attempts = attempts.clone();
|
||||
let answer = (*answer).clone();
|
||||
let attempt_index = attempt_index.clone();
|
||||
let result = result.clone();
|
||||
|
||||
move |k: Key| {}
|
||||
};
|
||||
UseBoardHandle {
|
||||
current_attempt,
|
||||
attempts,
|
||||
attempt_index,
|
||||
virtual_keyboard,
|
||||
result,
|
||||
answer,
|
||||
}
|
||||
}
|
130
src/main.rs
130
src/main.rs
|
@ -1,131 +1,61 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use crate::components::AttemptRow;
|
||||
use crate::components::{AttemptRow, Board};
|
||||
use crate::components::CurrentRow;
|
||||
use crate::components::EmptyRow;
|
||||
use crate::components::Keyboard;
|
||||
use crate::components::ResultBoard;
|
||||
use crate::consts::Key;
|
||||
use crate::consts::Result;
|
||||
use crate::consts::MAX_ATTEMPTS;
|
||||
use crate::services::words::{get_all_words, get_word_of_the_day};
|
||||
use chrono::Datelike;
|
||||
use crate::consts::{Key, MAX_ATTEMPTS};
|
||||
use crate::hooks::use_board;
|
||||
|
||||
use gloo::console;
|
||||
|
||||
mod components;
|
||||
mod consts;
|
||||
mod hooks;
|
||||
mod services;
|
||||
mod utils;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let answer = use_state(|| get_word_of_the_day());
|
||||
let all_the_words = use_memo(|_| get_all_words(), 0);
|
||||
let attempts: UseStateHandle<Vec<String>> = use_state(|| vec![String::default(); MAX_ATTEMPTS]);
|
||||
let attempt_index = use_state(|| 0usize);
|
||||
let current_attempt = use_state(|| String::new());
|
||||
let result = use_state(|| None::<Result>);
|
||||
let board = use_board();
|
||||
|
||||
let onkeypress = {
|
||||
console::log!(chrono::Utc::now().year());
|
||||
let current = current_attempt.clone();
|
||||
let answer = answer.clone();
|
||||
let answer_str = answer.clone().to_string();
|
||||
let current_str = current.clone().to_string();
|
||||
let attempts = attempts.clone();
|
||||
let result = result.clone();
|
||||
let index = attempt_index.clone();
|
||||
|
||||
move |event: KeyboardEvent| {
|
||||
console::log!(&event);
|
||||
|
||||
if result.is_some() {
|
||||
return {};
|
||||
}
|
||||
let board = board.clone();
|
||||
|
||||
Callback::from(move |event: KeyboardEvent| {
|
||||
let key_code = event.key_code();
|
||||
board.send_key(Key::from(key_code));
|
||||
console::log!(&event);
|
||||
})
|
||||
};
|
||||
|
||||
match Key::from(key_code) {
|
||||
Key::CharKey(c) => {
|
||||
if current.chars().count() >= 5 {
|
||||
return ();
|
||||
}
|
||||
let onclick = {
|
||||
let board = board.clone();
|
||||
|
||||
let mut new_attempt = current_str.clone();
|
||||
new_attempt.push(c);
|
||||
console::log!(&new_attempt);
|
||||
current.set(new_attempt);
|
||||
}
|
||||
Key::Backspace => {
|
||||
if current.is_empty() {
|
||||
return ();
|
||||
}
|
||||
|
||||
let mut new_attempt = current_str.clone();
|
||||
// This unwrap is safe because the potential failura it's already covered
|
||||
// by the upper if
|
||||
new_attempt.pop().unwrap();
|
||||
console::log!(&new_attempt);
|
||||
current.set(new_attempt);
|
||||
}
|
||||
Key::Enter => {
|
||||
if current.chars().count() != 5 {
|
||||
return;
|
||||
}
|
||||
|
||||
if !all_the_words.contains(¤t) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_attempts = attempts.clone().to_vec();
|
||||
new_attempts[*index] = current_str.clone();
|
||||
|
||||
attempts.set(new_attempts);
|
||||
current.set(String::default());
|
||||
index.set(*index + 1);
|
||||
|
||||
if ¤t_str == &answer_str {
|
||||
result.set(Some(Result::Win));
|
||||
} else if *index == MAX_ATTEMPTS {
|
||||
result.set(Some(Result::Fail));
|
||||
} else {
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Callback::from(move |key| {
|
||||
board.send_key(key);
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "Worsdle" }</h1>
|
||||
<div class="board" onkeyup={onkeypress} tabindex="0">
|
||||
{
|
||||
for attempts.clone().iter().enumerate().map(|(i, attempt)| {
|
||||
let current = current_attempt.clone().to_string();
|
||||
let attempt = attempt.clone();
|
||||
|
||||
if i < *attempt_index {
|
||||
let answer = answer.clone().to_string();
|
||||
|
||||
html! { <AttemptRow text={attempt} answer={answer} /> }
|
||||
} else if i == *attempt_index {
|
||||
html! { <CurrentRow text={ current } /> }
|
||||
} else {
|
||||
html! { <EmptyRow /> }
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div class={ format!("result {}", if result.is_some() { "" } else { "hidden" }) } >
|
||||
<Board
|
||||
attempts={ (*board.attempts).clone() }
|
||||
attempt_index={ *board.attempt_index.borrow() }
|
||||
current_attempt={ (*board.current_attempt).clone() }
|
||||
onkeypress={ onkeypress }
|
||||
/>
|
||||
<div>
|
||||
<span> {
|
||||
match *result {
|
||||
Some(Result::Win) => "Ganaste!!",
|
||||
Some(Result::Fail) => "Perdiste :c",
|
||||
_ => "No deberías ver esto"
|
||||
match (*board.result).clone() {
|
||||
Some(res) => html! { <ResultBoard attempts={(*board.attempts).clone()} /> },
|
||||
_ => html! { <Keyboard
|
||||
keyboard={ (board.virtual_keyboard).clone() }
|
||||
onclick={ onclick } /> }
|
||||
}
|
||||
} </span>
|
||||
< ResultBoard answer={ answer.to_string().clone() } attempts={(*attempts).clone()} />
|
||||
<p> { "Vuelve mañana para otro desafío :)" } </p>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
use crate::consts::{KeyboardKeyType, Status, VirtualKey};
|
||||
|
||||
pub fn new_empty_virtual_keyboard() -> [VirtualKey; 29] {
|
||||
[
|
||||
VirtualKey::from_charkey('Q'),
|
||||
VirtualKey::from_charkey('W'),
|
||||
VirtualKey::from_charkey('E'),
|
||||
VirtualKey::from_charkey('R'),
|
||||
VirtualKey::from_charkey('T'),
|
||||
VirtualKey::from_charkey('Y'),
|
||||
VirtualKey::from_charkey('U'),
|
||||
VirtualKey::from_charkey('I'),
|
||||
VirtualKey::from_charkey('O'),
|
||||
VirtualKey::from_charkey('P'),
|
||||
VirtualKey::from_charkey('A'),
|
||||
VirtualKey::from_charkey('S'),
|
||||
VirtualKey::from_charkey('D'),
|
||||
VirtualKey::from_charkey('F'),
|
||||
VirtualKey::from_charkey('G'),
|
||||
VirtualKey::from_charkey('H'),
|
||||
VirtualKey::from_charkey('J'),
|
||||
VirtualKey::from_charkey('K'),
|
||||
VirtualKey::from_charkey('L'),
|
||||
VirtualKey::from_charkey('Ñ'),
|
||||
VirtualKey::from_key(KeyboardKeyType::Backspace),
|
||||
VirtualKey::from_charkey('Z'),
|
||||
VirtualKey::from_charkey('X'),
|
||||
VirtualKey::from_charkey('C'),
|
||||
VirtualKey::from_charkey('V'),
|
||||
VirtualKey::from_charkey('B'),
|
||||
VirtualKey::from_charkey('N'),
|
||||
VirtualKey::from_charkey('M'),
|
||||
VirtualKey::from_key(KeyboardKeyType::Enter),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn evaluate_status(ans: char, attr: char, answer: String) -> Status {
|
||||
if ans == attr {
|
||||
Status::Found
|
||||
} else if answer.contains(attr) {
|
||||
Status::Almost
|
||||
} else {
|
||||
Status::NotFound
|
||||
}
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
|
@ -82,3 +82,16 @@ body {
|
|||
width: 20px;
|
||||
|
||||
}
|
||||
|
||||
.keyboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(20, 1fr);
|
||||
}
|
||||
|
||||
.keyboard > button {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.keyboard > button.key-big {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue