diff --git a/src/css/icons.css b/src/css/icons.css
new file mode 100644
index 0000000..58ee1b8
--- /dev/null
+++ b/src/css/icons.css
@@ -0,0 +1,23 @@
+@font-face {
+ font-family: 'Material Symbols Rounded';
+ font-style: normal;
+ font-weight: 100 700;
+ src: url(./icons.woff2) format('woff2');
+}
+
+.material-symbols-rounded {
+ font-family: 'Material Symbols Rounded';
+ font-weight: normal;
+ font-style: normal;
+ font-size: 24px;
+ overflow: hidden;
+ line-height: 1;
+ letter-spacing: normal;
+ text-transform: none;
+ display: inline-block;
+ white-space: nowrap;
+ word-wrap: normal;
+ direction: ltr;
+ -webkit-font-feature-settings: 'liga';
+ -webkit-font-smoothing: antialiased;
+}
\ No newline at end of file
diff --git a/src/css/icons.woff2 b/src/css/icons.woff2
new file mode 100644
index 0000000..16a9d96
Binary files /dev/null and b/src/css/icons.woff2 differ
diff --git a/src/index.html b/src/index.html
index edf25b1..d171099 100644
--- a/src/index.html
+++ b/src/index.html
@@ -5,6 +5,7 @@
Home
+
@@ -29,7 +30,8 @@
-->
-
+
+
diff --git a/src/js/abstract.js b/src/js/abstract.js
deleted file mode 100644
index 1665493..0000000
--- a/src/js/abstract.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * @typedef {Object} $
- * @property {function(object=, ...HTMLElement): HTMLElement} DOCS - creates element with this tagname
-*/
-
-/**
- * ### Create element
- *
- * @type {$}
- */
-const $ = new Proxy(
- class {
- /**
- * ### Creates `$` element
- * @param {string} type element type
- * @param {object} props element properties (onclick, class, ...)
- * @param {...HTMLElement} children
- * @returns {HTMLElement}
- */
- static createElement(type, props, ...children) {
- const element = document.createElement(type)
-
- if (props) {
- try {
- if (
- props instanceof Node
- || typeof props === 'string'
- || typeof props === 'number'
- ) children = [props, ...children]
- else {throw Error()}
- } catch {
- for (const [key, value] of Object.entries(props)) {
- if (key.startsWith('on') && typeof value === 'function') {
- element.addEventListener(key.slice(2).toLowerCase(), value)
- } else if (key === 'className') {
- element.className = value
- } else {
- element.setAttribute(key, value)
- }
- }
- }
- }
- children.flat().forEach(child => {
- if (typeof child === 'string' || typeof child === 'number') {
- element.innerHTML += child
- } else if (child instanceof Node) {
- element.appendChild(child)
- }
- });
-
- element[Symbol.toPrimitive] = function(hint) {
- if (hint === 'string') return this.outerHTML
- return this
- };
-
- return element
- }
- },
- {
- get(target, element) {
- return function(props, ...children) {
- return target.createElement(element, props, ...children)
- }
- }
- }
-);
-
diff --git a/src/lib/gicons.lib.js b/src/lib/gicons.lib.js
new file mode 100644
index 0000000..d22d835
--- /dev/null
+++ b/src/lib/gicons.lib.js
@@ -0,0 +1,56 @@
+/**
+ * @typedef {Object} IconOptions
+ * @prop {100|200|300|400|500|600|700} weight Symbol weight
+ * @prop {bool} fill
+ * @prop {'low' | 'default' | 'high'} grade Symbol thickness
+ * @prop {20|48} optical_size (from 20 to 48) For the image to look the same at different sizes in dp
+ * @prop {number} size Icon size in px
+ * @prop {string} color Any css-supported color variant (names, rgb, hex)
+ */
+
+var DEFAULT_ICON_COLOR = '#000'
+var DEFAULT_ICON_SIZE = 24
+/** @type {IconOptions} */
+var DEFAULT_ICON_OPTIONS = {}
+
+/** @type {Record HTMLSpanElement} */
+const Icons = new Proxy(
+ class {
+ /**
+ * @param {string} name
+ * @param {IconOptions} options
+ * @returns {HTMLSpanElement}
+ */
+ static getIcon(name, {fill, weight, grade, optical_size, size, color}) {
+ const gr = ({
+ 'low': -25,
+ 'default': 0,
+ 'high': 200
+ })[grade]
+ const data = `
+ 'FILL' ${fill !== undefined ? fill?1:0 : 1},
+ 'wdth' ${weight || 400},
+ 'GRAD' ${gr || 0},
+ 'opsz' ${optical_size || 20}`
+ const fz = size ? typeof size != Number ? `${size}px`:size : (DEFAULT_ICON_SIZE+'px')
+ return $.span(
+ {class: 'material-symbols-rounded icon', style: `font-size: ${fz}; width: ${fz}; height: ${fz}; ${color ? `color: ${color};` : ''}font-variation-settings: ${data}`},
+ name
+ )
+ }
+ },
+ {
+ get(target, name) {
+ /**
+ * @argument {IconOptions} options
+ */
+ return function(options) {
+ return target.getIcon(name, options || DEFAULT_ICON_OPTIONS)
+ }
+ }
+ }
+)
+
+const gIconsInit = () => document.head.innerHTML += `
+
+`
\ No newline at end of file
diff --git a/src/lib/sinya.lib.js b/src/lib/sinya.lib.js
new file mode 100644
index 0000000..aa357ae
--- /dev/null
+++ b/src/lib/sinya.lib.js
@@ -0,0 +1,226 @@
+/**
+ * @typedef {Object} $
+ * @property {function(object=, ...HTMLElement): HTMLElement} div - creates div element
+ * @property {function(object=, ...HTMLElement): HTMLElement} p - creates p element
+ * @property {function(object=, ...HTMLElement): HTMLElement} span - creates span element
+ * @property {function(object=, ...HTMLElement): HTMLElement} button - creates button element
+ * @property {function(object=, ...HTMLElement): HTMLElement} input - creates input element
+ * @property {function(object=, ...HTMLElement): HTMLElement} a - creates a element
+ * @property {function(object=, ...HTMLElement): HTMLElement} img - creates img element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h1 - creates h1 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h2 - creates h2 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h3 - creates h3 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h4 - creates h4 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h5 - creates h5 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} h6 - creates h6 element
+ * @property {function(object=, ...HTMLElement): HTMLElement} ul - creates ul element
+ * @property {function(object=, ...HTMLElement): HTMLElement} li - creates li element
+ * @property {function(object=, ...HTMLElement): HTMLElement} form - creates form element
+ * @property {function(object=, ...HTMLElement): HTMLElement} table - creates table element
+ * @property {function(object=, ...HTMLElement): HTMLElement} tr - creates tr element
+ * @property {function(object=, ...HTMLElement): HTMLElement} td - creates td element
+*/
+
+/**
+ * ### Create element
+ *
+ * @type {$}
+ */
+const $ = new Proxy(
+ class {
+ /**
+ * ### Creates `$` element
+ * @param {string} type element type
+ * @param {object} props element properties (onclick, class, ...)
+ * @param {...HTMLElement} children
+ * @returns {HTMLElement}
+ */
+ static createElement(type, props, ...children) {
+ const element = document.createElement(type)
+
+ if (props) {
+ try {
+ if (
+ props instanceof Node
+ || typeof props === 'string'
+ || typeof props === 'number'
+ ) children = [props, ...children]
+ else {throw Error()}
+ } catch {
+ for (const [key, value] of Object.entries(props)) {
+ if (key.startsWith('on') && typeof value === 'function') {
+ element.addEventListener(key.slice(2).toLowerCase(), value)
+ } else if (key === 'className') {
+ element.className = value
+ } else {
+ element.setAttribute(key, value)
+ }
+ }
+ }
+ }
+ children.flat().forEach(child => {
+ if (typeof child === 'string' || typeof child === 'number') {
+ element.innerHTML += child
+ } else if (child instanceof Node) {
+ element.appendChild(child)
+ }
+ });
+
+ element[Symbol.toPrimitive] = function(hint) {
+ if (hint === 'string') return this.outerHTML
+ return this
+ };
+
+ return element
+ }
+ },
+ {
+ get(target, element) {
+ return function(props, ...children) {
+ return target.createElement(element, props, ...children)
+ }
+ }
+ }
+);
+
+
+/**
+ * Select single DOM element and wrap them
+ * @param {string} selector
+ * @returns {WrappedElement}
+ */
+const select = (selector, _target = document) => wrap(_target.querySelector(selector))
+
+/**
+ * Select multiple DOM element and wrap them
+ * @param {string} selector
+ * @returns {Array.}
+ */
+const selectAll = (selector, _target = document) => [..._target.querySelectorAll(selector)].map(_ => wrap(_))
+
+/**
+ * Wrap existing DOM element
+ * @param {HTMLElement} element
+ * @returns {WrappedElement}
+ */
+const wrap = element => new WrappedElement(element)
+
+const sleep = s => new Promise(r => setTimeout(r, s*1000))
+
+
+class WrappedElement {
+ /**
+ * @type {HTMLElement}
+ */
+ element
+
+ /**
+ * Create element wrapper
+ * @param {HTMLElement} element
+ */
+ constructor(element) {
+ this.element = element
+ }
+
+ /**
+ * Override element classes
+ * @param {...string} list
+ * @returns {WrappedElement} this element for chaining
+ */
+ classes(...list) {
+ this.element.classList = list.join(' ')
+ return this
+ }
+
+ /**
+ * Get elements styles to edit them
+ * @returns {CSSStyleDeclaration}
+ */
+ styles () {
+ return this.element.style
+ }
+
+ /**
+ * Get element nodes
+ * @returns {Array.}
+ */
+ nodes () {
+ return [...this.element.childNodes].map(e => wrap(e))
+ }
+
+ /**
+ * Apply animation to element (async)
+ * @param {string} animation_name
+ * @param {number} animation_duration
+ * @param {string} animation_function_name
+ * @returns {WrappedElement} this element for chaining
+ */
+ async animate (animation_name, animation_duration, animation_function_name = '') {
+ this.styles().animation = `${animation_name} ${animation_duration}s ${animation_function_name}`
+ await sleep(animation_duration - (animation_duration / 20))
+ this.styles().animation = ``
+ return this
+ }
+
+ /**
+ * Clear element content (async)
+ * @returns {WrappedElement} this element for chaining
+ */
+ async clear () {
+ this.element.innerHTML = ''
+ return this
+ }
+
+ /**
+ * Overwrite element content (async)
+ * @param {...HTMLElement} children
+ * @returns {WrappedElement} this element for chaining
+ */
+ async overwrite (...children) {
+ this.clear()
+ this.append(...children)
+ return this
+ }
+ /**
+ * Add content to element (async)
+ * @param {...HTMLElement} children
+ * @returns {WrappedElement} this element for chaining
+ */
+ async append (...children) {
+ children.forEach(child => {
+ if (typeof child === 'string' || typeof child === 'number') {
+ this.element.innerHTML += child
+ } else if (child instanceof Node) {
+ this.element.appendChild(child)
+ } else if (child instanceof WrappedElement) {
+ this.element.appendChild(child.element)
+ }
+ })
+ return this
+ }
+ /**
+ * Removes element (async)
+ */
+ async remove() {
+ this.element.remove()
+ }
+
+ /**
+ * Return element node by selector
+ * @param {string} selector
+ * @returns {WrappedElement}
+ */
+ select (selector) {
+ return select(selector, this.element)
+ }
+ /**
+ * Return element nodes by selector
+ * @param {string} selector
+ * @returns {Array.}
+ */
+ selectAll (selector) {
+ return selectAll(selector, this.element)
+ }
+}
+
+const Body = select('body')
\ No newline at end of file