mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
wip: add an image
This commit is contained in:
parent
dc34805ee6
commit
7b29fbbad0
8 changed files with 331 additions and 174 deletions
14
README.md
14
README.md
|
@ -12,15 +12,21 @@ You can use html-esque semantics with stylesheets, inline styles, tree hierarchy
|
|||
|
||||
static App: FC<()> = |cx| {
|
||||
cx.render(rsx!{
|
||||
div { width: "100%", height: "3px", border_style: "solid",
|
||||
h1 { "Hello world!" }
|
||||
p { "This is a paragraph." }
|
||||
div {
|
||||
width: "100%",
|
||||
height: "10px",
|
||||
background_color: "red",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
|
||||
|
||||
"Hello world!"
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
an image should go here
|
||||
![demo app](examples/example.png)
|
||||
|
||||
|
||||
Rink is basically a port of [Ink]() but for Rust and Dioxus. Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
|
||||
|
|
BIN
examples/example.png
Normal file
BIN
examples/example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
59
examples/margin.rs
Normal file
59
examples/margin.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
dom.rebuild();
|
||||
|
||||
rink::render_vdom(&dom).unwrap();
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "column",
|
||||
background_color: "black",
|
||||
// margin_right: "10px",
|
||||
|
||||
|
||||
|
||||
div {
|
||||
width: "70%",
|
||||
height: "70%",
|
||||
// margin_left: "4px",
|
||||
background_color: "green",
|
||||
|
||||
div {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
|
||||
|
||||
margin_top: "2px",
|
||||
margin_bottom: "2px",
|
||||
margin_left: "2px",
|
||||
margin_right: "2px",
|
||||
// flex_shrink: "0",
|
||||
|
||||
background_color: "red",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
flex_direction: "column",
|
||||
|
||||
|
||||
// padding_top: "2px",
|
||||
// padding_bottom: "2px",
|
||||
// padding_left: "4px",
|
||||
// padding_right: "4px",
|
||||
|
||||
|
||||
"[A]"
|
||||
"[A]"
|
||||
"[A]"
|
||||
"[A]"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
22
examples/readme.rs
Normal file
22
examples/readme.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
dom.rebuild();
|
||||
|
||||
rink::render_vdom(&dom).unwrap();
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "10px",
|
||||
background_color: "red",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
|
||||
"Hello world!"
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
- [ ] pub display: Display,
|
||||
- [ ] pub position_type: PositionType,
|
||||
- [x] pub position_type: PositionType, --> kinda, stretch doesnt support everything
|
||||
- [ ] pub direction: Direction,
|
||||
|
||||
- [x] pub flex_direction: FlexDirection,
|
||||
|
@ -9,26 +9,27 @@
|
|||
- [x] pub flex_shrink: f32,
|
||||
- [x] pub flex_basis: Dimension,
|
||||
|
||||
- [ ] pub overflow: Overflow,
|
||||
- [x] pub overflow: Overflow, ---> kinda implemented... stretch doesnt have support for directional overflow
|
||||
|
||||
- [x] pub align_items: AlignItems,
|
||||
- [x] pub align_self: AlignSelf,
|
||||
- [x] pub align_content: AlignContent,
|
||||
|
||||
- [ ] pub margin: Rect<Dimension>,
|
||||
- [ ] pub padding: Rect<Dimension>,
|
||||
- [x] pub margin: Rect<Dimension>,
|
||||
- [x] pub padding: Rect<Dimension>,
|
||||
|
||||
- [x] pub justify_content: JustifyContent,
|
||||
- [ ] pub position: Rect<Dimension>,
|
||||
- [ ] pub border: Rect<Dimension>,
|
||||
- [ ] pub size: Size<Dimension>,
|
||||
|
||||
- [ ] pub size: Size<Dimension>, ----> ??? seems to only be relevant for input?
|
||||
- [ ] pub min_size: Size<Dimension>,
|
||||
- [ ] pub max_size: Size<Dimension>,
|
||||
|
||||
- [ ] pub aspect_ratio: Number,
|
||||
*/
|
||||
|
||||
use stretch2::{prelude::*, style::Style};
|
||||
use stretch2::{prelude::*, style::PositionType, style::Style};
|
||||
use tui::style::Style as TuiStyle;
|
||||
|
||||
pub struct StyleModifer {
|
||||
|
@ -137,7 +138,13 @@ pub fn apply_attributes(
|
|||
"counter-reset" => {}
|
||||
|
||||
"cursor" => {}
|
||||
"direction" => {}
|
||||
"direction" => {
|
||||
match value {
|
||||
"ltr" => style.style.direction = Direction::LTR,
|
||||
"rtl" => style.style.direction = Direction::RTL,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
"display" => apply_display(name, value, style),
|
||||
|
||||
|
@ -231,8 +238,20 @@ pub fn apply_attributes(
|
|||
"perspective"
|
||||
| "perspective-origin" => {}
|
||||
|
||||
"position" => {}
|
||||
"position" => {
|
||||
match value {
|
||||
"static" => {}
|
||||
"relative" => style.style.position_type = PositionType::Relative,
|
||||
"fixed" => {}
|
||||
"absolute" => style.style.position_type = PositionType::Absolute,
|
||||
"sticky" => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"pointer-events" => {}
|
||||
|
||||
"quotes" => {}
|
||||
"resize" => {}
|
||||
"right" => {}
|
||||
|
@ -285,6 +304,29 @@ pub fn apply_attributes(
|
|||
}
|
||||
}
|
||||
|
||||
enum UnitSystem {
|
||||
Percent(f32),
|
||||
Point(f32),
|
||||
}
|
||||
|
||||
fn parse_value(value: &str) -> Option<UnitSystem> {
|
||||
if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
Some(UnitSystem::Point(px))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if value.ends_with("%") {
|
||||
if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
|
||||
Some(UnitSystem::Percent(pct))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
// todo: add more overflow support to stretch2
|
||||
|
@ -491,54 +533,35 @@ fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
|
|||
}
|
||||
|
||||
fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
// // left
|
||||
// start: stretch::style::Dimension::Points(10f32),
|
||||
|
||||
// // right?
|
||||
// end: stretch::style::Dimension::Points(10f32),
|
||||
|
||||
// // top?
|
||||
// // top: stretch::style::Dimension::Points(10f32),
|
||||
|
||||
// // bottom?
|
||||
// // bottom: stretch::style::Dimension::Points(10f32),
|
||||
|
||||
match name {
|
||||
"padding" => {
|
||||
if name.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.bottom = Dimension::Points(px);
|
||||
style.style.padding.top = Dimension::Points(px);
|
||||
style.style.padding.start = Dimension::Points(px);
|
||||
style.style.padding.end = Dimension::Points(px);
|
||||
}
|
||||
} else if name.ends_with("%") {
|
||||
if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
|
||||
//
|
||||
}
|
||||
match parse_value(value) {
|
||||
Some(UnitSystem::Percent(v)) => match name {
|
||||
"padding" => {
|
||||
let v = Dimension::Percent(v / 100.0);
|
||||
style.style.padding.top = v;
|
||||
style.style.padding.bottom = v;
|
||||
style.style.padding.start = v;
|
||||
style.style.padding.end = v;
|
||||
}
|
||||
}
|
||||
"padding-bottom" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.bottom = Dimension::Points(px);
|
||||
"padding-bottom" => style.style.padding.bottom = Dimension::Percent(v / 100.0),
|
||||
"padding-left" => style.style.padding.start = Dimension::Percent(v / 100.0),
|
||||
"padding-right" => style.style.padding.end = Dimension::Percent(v / 100.0),
|
||||
"padding-top" => style.style.padding.top = Dimension::Percent(v / 100.0),
|
||||
_ => {}
|
||||
},
|
||||
Some(UnitSystem::Point(v)) => match name {
|
||||
"padding" => {
|
||||
style.style.padding.top = Dimension::Points(v);
|
||||
style.style.padding.bottom = Dimension::Points(v);
|
||||
style.style.padding.start = Dimension::Points(v);
|
||||
style.style.padding.end = Dimension::Points(v);
|
||||
}
|
||||
}
|
||||
"padding-left" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.start = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
"padding-right" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.end = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
"padding-top" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.top = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
"padding-bottom" => style.style.padding.bottom = Dimension::Points(v),
|
||||
"padding-left" => style.style.padding.start = Dimension::Points(v),
|
||||
"padding-right" => style.style.padding.end = Dimension::Points(v),
|
||||
"padding-top" => style.style.padding.top = Dimension::Points(v),
|
||||
_ => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,12 +622,34 @@ pub fn apply_size(name: &str, value: &str, style: &mut StyleModifer) {
|
|||
}
|
||||
|
||||
pub fn apply_margin(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"margin" => {}
|
||||
"margin-bottom" => {}
|
||||
"margin-left" => {}
|
||||
"margin-right" => {}
|
||||
"margin-top" => {}
|
||||
_ => {}
|
||||
match parse_value(value) {
|
||||
Some(UnitSystem::Percent(v)) => match name {
|
||||
"margin" => {
|
||||
let v = Dimension::Percent(v / 100.0);
|
||||
style.style.margin.top = v;
|
||||
style.style.margin.bottom = v;
|
||||
style.style.margin.start = v;
|
||||
style.style.margin.end = v;
|
||||
}
|
||||
"margin-top" => style.style.margin.top = Dimension::Percent(v / 100.0),
|
||||
"margin-bottom" => style.style.margin.bottom = Dimension::Percent(v / 100.0),
|
||||
"margin-left" => style.style.margin.start = Dimension::Percent(v / 100.0),
|
||||
"margin-right" => style.style.margin.end = Dimension::Percent(v / 100.0),
|
||||
_ => {}
|
||||
},
|
||||
Some(UnitSystem::Point(v)) => match name {
|
||||
"margin" => {
|
||||
style.style.margin.top = Dimension::Points(v);
|
||||
style.style.margin.bottom = Dimension::Points(v);
|
||||
style.style.margin.start = Dimension::Points(v);
|
||||
style.style.margin.end = Dimension::Points(v);
|
||||
}
|
||||
"margin-top" => style.style.margin.top = Dimension::Points(v),
|
||||
"margin-bottom" => style.style.margin.bottom = Dimension::Points(v),
|
||||
"margin-left" => style.style.margin.start = Dimension::Points(v),
|
||||
"margin-right" => style.style.margin.end = Dimension::Points(v),
|
||||
_ => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,12 @@ use crate::{
|
|||
TuiNode,
|
||||
};
|
||||
|
||||
/*
|
||||
The layout system uses the lineheight as one point.
|
||||
|
||||
stretch uses fractional points, so we can rasterize if we need too, but not with characters
|
||||
this means anything thats "1px" is 1 lineheight. Unfortunately, text cannot be smaller or bigger
|
||||
*/
|
||||
pub fn collect_layout<'a>(
|
||||
layout: &mut stretch2::Stretch,
|
||||
nodes: &mut HashMap<ElementId, TuiNode<'a>>,
|
||||
|
@ -17,7 +23,6 @@ pub fn collect_layout<'a>(
|
|||
|
||||
match node {
|
||||
VNode::Text(t) => {
|
||||
//
|
||||
let id = t.id.get().unwrap();
|
||||
let char_len = t.text.chars().count();
|
||||
|
||||
|
@ -29,7 +34,6 @@ pub fn collect_layout<'a>(
|
|||
// text is as long as it is declared
|
||||
width: Dimension::Points(char_len as f32),
|
||||
},
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
193
src/lib.rs
193
src/lib.rs
|
@ -1,11 +1,16 @@
|
|||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use dioxus::core::*;
|
||||
use std::{collections::HashMap, io};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
sync::mpsc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use stretch2::{prelude::Size, Stretch};
|
||||
use tui::{backend::CrosstermBackend, style::Style as TuiStyle, Terminal};
|
||||
|
||||
|
@ -24,21 +29,6 @@ pub struct TuiNode<'a> {
|
|||
}
|
||||
|
||||
pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
|
||||
/*
|
||||
-> collect all the nodes with their layout
|
||||
-> solve their layout
|
||||
-> render the nodes in the right place with tui/crosstream
|
||||
-> while rendering, apply styling
|
||||
|
||||
use simd to compare lines for diffing?
|
||||
|
||||
*/
|
||||
let mut layout = Stretch::new();
|
||||
let mut nodes = HashMap::new();
|
||||
|
||||
let root_node = vdom.base_scope().root_node();
|
||||
layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
|
||||
|
||||
/*
|
||||
Get the terminal to calcualte the layout from
|
||||
*/
|
||||
|
@ -52,30 +42,90 @@ pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
|
|||
let backend = CrosstermBackend::new(io::stdout());
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
|
||||
let dims = terminal.size().unwrap();
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
// Setup input handling
|
||||
let (tx, rx) = mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
let tick_rate = Duration::from_millis(100);
|
||||
let mut last_tick = Instant::now();
|
||||
loop {
|
||||
// poll for tick rate duration, if no events, sent tick event.
|
||||
let timeout = tick_rate
|
||||
.checked_sub(last_tick.elapsed())
|
||||
.unwrap_or_else(|| Duration::from_secs(0));
|
||||
|
||||
/*
|
||||
Compute the layout given th terminal size
|
||||
*/
|
||||
let node_id = root_node.try_mounted_id().unwrap();
|
||||
let root_layout = nodes[&node_id].layout;
|
||||
layout.compute_layout(
|
||||
root_layout,
|
||||
Size {
|
||||
width: stretch2::prelude::Number::Defined(width as f32),
|
||||
height: stretch2::prelude::Number::Defined(height as f32),
|
||||
},
|
||||
)?;
|
||||
if event::poll(timeout).unwrap() {
|
||||
if let TermEvent::Key(key) = event::read().unwrap() {
|
||||
tx.send(InputEvent::UserInput(key)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
terminal.draw(|frame| {
|
||||
//
|
||||
render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
|
||||
assert!(nodes.is_empty());
|
||||
})?;
|
||||
if last_tick.elapsed() >= tick_rate {
|
||||
tx.send(InputEvent::Tick).unwrap();
|
||||
last_tick = Instant::now();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(5000));
|
||||
terminal.clear().unwrap();
|
||||
|
||||
loop {
|
||||
let dims = terminal.size().unwrap();
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
|
||||
/*
|
||||
-> collect all the nodes with their layout
|
||||
-> solve their layout
|
||||
-> render the nodes in the right place with tui/crosstream
|
||||
-> while rendering, apply styling
|
||||
|
||||
use simd to compare lines for diffing?
|
||||
|
||||
*/
|
||||
let mut layout = Stretch::new();
|
||||
let mut nodes = HashMap::new();
|
||||
|
||||
let root_node = vdom.base_scope().root_node();
|
||||
layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
|
||||
/*
|
||||
Compute the layout given th terminal size
|
||||
*/
|
||||
let node_id = root_node.try_mounted_id().unwrap();
|
||||
let root_layout = nodes[&node_id].layout;
|
||||
layout.compute_layout(
|
||||
root_layout,
|
||||
Size {
|
||||
width: stretch2::prelude::Number::Defined(width as f32),
|
||||
height: stretch2::prelude::Number::Defined(height as f32),
|
||||
},
|
||||
)?;
|
||||
|
||||
terminal.draw(|frame| {
|
||||
//
|
||||
render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
|
||||
assert!(nodes.is_empty());
|
||||
})?;
|
||||
|
||||
match rx.recv()? {
|
||||
InputEvent::UserInput(event) => match event.code {
|
||||
KeyCode::Char('q') => {
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
break;
|
||||
}
|
||||
_ => {} // handle event
|
||||
},
|
||||
InputEvent::Tick => {} // tick
|
||||
InputEvent::Close => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
|
@ -88,65 +138,8 @@ pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// enum InputEvent {
|
||||
// UserInput(KeyEvent),
|
||||
// Close,
|
||||
// Tick,
|
||||
// }
|
||||
|
||||
// // Setup input handling
|
||||
// let (tx, rx) = mpsc::channel();
|
||||
// let tick_rate = Duration::from_millis(100);
|
||||
// thread::spawn(move || {
|
||||
// let mut last_tick = Instant::now();
|
||||
// loop {
|
||||
// // poll for tick rate duration, if no events, sent tick event.
|
||||
// let timeout = tick_rate
|
||||
// .checked_sub(last_tick.elapsed())
|
||||
// .unwrap_or_else(|| Duration::from_secs(0));
|
||||
|
||||
// if event::poll(timeout).unwrap() {
|
||||
// if let TermEvent::Key(key) = event::read().unwrap() {
|
||||
// tx.send(InputEvent::UserInput(key)).unwrap();
|
||||
// }
|
||||
// }
|
||||
|
||||
// // if last_tick.elapsed() >= tick_rate {
|
||||
// // tx.send(InputEvent::Tick).unwrap();
|
||||
// // last_tick = Instant::now();
|
||||
// // }
|
||||
// }
|
||||
// });
|
||||
|
||||
// terminal.clear()?;
|
||||
|
||||
// // loop {
|
||||
// terminal.draw(|frame| {
|
||||
// // draw the frame
|
||||
// let size = frame.size();
|
||||
// let root_node = vdom.base_scope().root_node();
|
||||
// let block = render_vnode(frame, vdom, root_node);
|
||||
|
||||
// frame.render_widget(block, size);
|
||||
// })?;
|
||||
|
||||
// // // terminal.draw(|f| ui::draw(f, &mut app))?;
|
||||
// match rx.recv()? {
|
||||
// InputEvent::UserInput(event) => match event.code {
|
||||
// KeyCode::Char('q') => {
|
||||
// disable_raw_mode()?;
|
||||
// execute!(
|
||||
// terminal.backend_mut(),
|
||||
// LeaveAlternateScreen,
|
||||
// DisableMouseCapture
|
||||
// )?;
|
||||
// terminal.show_cursor()?;
|
||||
// // break;
|
||||
// }
|
||||
// _ => {} // handle event
|
||||
// },
|
||||
// InputEvent::Tick => {} // tick
|
||||
// InputEvent::Close => {
|
||||
// // break;
|
||||
// }
|
||||
// };
|
||||
enum InputEvent {
|
||||
UserInput(KeyEvent),
|
||||
Close,
|
||||
Tick,
|
||||
}
|
||||
|
|
42
test.html
42
test.html
|
@ -14,32 +14,60 @@
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: black;
|
||||
/* justify-content: center;
|
||||
align-items: center; */
|
||||
/* margin: auto; */
|
||||
}
|
||||
|
||||
.smaller {
|
||||
height: 50%;
|
||||
height: 70%;
|
||||
width: 70%;
|
||||
background-color: green;
|
||||
/* justify-content: center; */
|
||||
/* align-items: center; */
|
||||
}
|
||||
|
||||
.superinner {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
/* display: flex; */
|
||||
/* */
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
/* */
|
||||
background-color: red;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: violet;
|
||||
flex-direction: column;
|
||||
/* margin: 20px; */
|
||||
/* margin: 20px; */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="smaller">
|
||||
<div class="superinner">
|
||||
<h1>Hello World</h1>
|
||||
<p>This is a test</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="container">
|
||||
<div class="smaller">
|
||||
hello world
|
||||
<div style="color: red;">
|
||||
<div style="color: green; margin: 40px;">
|
||||
goodbye
|
||||
<div>
|
||||
<div style="color:red;">
|
||||
asdasdasd
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in a new issue