commit 52ae011cb66980b8ad2657ac8901cf289e174ebf Author: Bashynx Date: Fri Mar 27 16:47:50 2026 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b823bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +_config.yml + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b3ff301 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The Hackman Club + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e8bb376 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Tapestry-Code Theme + +Tapestry-Code is a Hexo theme derrived from Tapestry theme I am using for personal blog. + +I am using Tapestry-Code for my [technical blog](https://blog.hackman.club/). + +--- + +## 1. Installation + +1. Clone the theme into your Hexo project: + +```bash +cd your-hexo-project +git clone https://github.com/HackmanClub/tapestry-code.git themes/tapestry-code +``` + +1. Set the theme in Hexo’s main `_config.yml`: + +```yaml +theme: tapestry-code +``` + +--- + +## 2. Configuration + +The theme uses its own `_config.yml` inside `themes/tapestry/`. You will need to copy / rename `_EXAMPLE_config.yml` as a starting point: + +```bash +cp themes/tapestry/_EXAMPLE_config.yml themes/tapestry/_config.yml +``` + +### Important Sections + +#### Site Settings + +- `avatar`, `banner`, `favicon`: paths to your images. +- `primary_color`: main color for your theme - it's used for links and avatar border. +- `secondary_color`: secondary color for your theme - it's used for links (mostly hover). +- `sidebar`: show or hide the sidebar. +- `scarves`: enable or disable scarves links. + +#### Scarves + +Scarf links appear at the top right. You can use an image or FontAwesome icon: + +```yaml +scarf: + - url: tags/baking + image: images/assets/scarves/scarf.png + icon: false + enable: true + alt: Example + - url: tags/photo + image: false + icon: fa-solid fa-heart + enable: true + alt: Example +``` + +- `url` – link target +- `image` – path to scarf image +- `icon` – FontAwesome icon class +- `enable` – show/hide scarf +- `alt` – accessibility text + +#### Social Links + +Add or remove social links easily: + +```yaml +social: + - url: https://www.instagram.com/ + icon: fab fa-instagram + relme: true + enable: true +``` + +If `relme: true` in config, the corresponding social media entry will have `rel="me"`, that you need for verifying sites (eg, mastodon). + +--- + +### Sidebar Menu + +Configure links in the sidebar: + +```yaml +sidebar_menu: + - enable: true + name: Home + url: / + - enable: true + name: Get Hexo + url: https://hexo.io/docs/setup +``` + +- `enable` – show/hide +- `name` – link text +- `url` – destination + +--- + +### Blog images + +Define image as: + +`photos: images/image.png` + +> ⚠️ Image shows only if `post.photos` exists, and only first image will be shown. + +--- + +## 4. Running Locally + +Start a local Hexo server to preview: + +```bash +hexo clean +hexo s +``` + +- Visit `http://localhost:4000` +- Changes in templates or CSS are reflected immediately. diff --git a/_EXAMPLE_config.yml b/_EXAMPLE_config.yml new file mode 100644 index 0000000..db53bb9 --- /dev/null +++ b/_EXAMPLE_config.yml @@ -0,0 +1,85 @@ +name: Tapestry-Code +version: 1 +copy_year: 2026 + +# Site configuration +avatar: images/assets/avatar.jpg # Avatar image, also fontawesome fallback +banner: images/assets/banner.png +favicon: images/assets/avatar.jpg +fontawesome: https://kit.fontawesome.com/bae8420862.js # Use your own kit here +primary_color: '#ffc107' +secondary_color: '#296126' +sidebar: true # Display sidebar +scarves: true # Display scarves + +#Footer +hexo: true # Display "Powered by Hexo" in footer +copyright: true # Display copyright + +# Scarves (links on top right of screen) (add more / remove as needed) +# Defaults into FontAwesome if no image provided +# Disables scarf if no image, and font awesome not configured +scarf: + - url: tags/baking # Example Usecase for image + image: images/assets/scarves/scarf.png + icon: false + enable: true + color: false + alt: Example + - url: tags/photo # Example Usecase for FontAwesome + image: false + icon: fa-solid fa-heart + enable: true + color: false + alt: Example + +# Enable or disable social links (add more / remove as needed) +social: + - url: https://www.instagram.com/ + icon: fab fa-instagram + relme: true + enable: true + - url: https://discord.com/ + icon: fab fa-discord + relme: true + enable: true + - url: https://www.twitch.tv/ + icon: fab fa-twitch + enable: false + - url: https://mastodon.social + icon: fab fa-mastodon + enable: true + - url: https://www.linkedin.com/ + icon: fab fa-linkedin + enable: true + - url: https://www.twitter.com/ + icon: fab fa-twitter + enable: true + - url: https://www.facebook.com/ + icon: fab fa-facebook + enable: false + - url: https://www.youtube.com/ + icon: fab fa-youtube + enable: false + - url: https://www.exapmle.com/ + icon: fab fa-hashtag + enable: false + +# Sidebar menu configuration (add more / remove as needed) +sidebar_menu: + - enable: true + name: Home + url: / + - enable: true + name: Get Hexo + url: https://hexo.io/docs/setup + - enable: true + name: Get Tapestry + url: https://tapestry.demo.bashynx.dev + - enable: true + name: Bashynx + url: https://bashynx.com + - enable: true + name: The Hackman Club + url: https://blog.hackman.club + diff --git a/layout/_partial/date.ejs b/layout/_partial/date.ejs new file mode 100644 index 0000000..1829d5f --- /dev/null +++ b/layout/_partial/date.ejs @@ -0,0 +1,19 @@ +<% { + const posted = new Date(date); + const now = new Date(); + const day = ((d => { + if (d > 3 && d < 21) return d + 'th'; + switch (d % 10) { + case 1: return d + 'st'; + case 2: return d + 'nd'; + case 3: return d + 'rd'; + default: return d + 'th'; + } + })(posted.getDate())); + + const options = now.getFullYear() === posted.getFullYear() + ? { month: 'long', hour: 'numeric', minute: 'numeric', hour12: true } + : { year: 'numeric', month: 'long'}; +%> + <%= day %> <%= posted.toLocaleString('en-US', options).replace(/^(\d+)\s/, '') %> +<% } %> \ No newline at end of file diff --git a/layout/_partial/feed.ejs b/layout/_partial/feed.ejs new file mode 100644 index 0000000..4e18264 --- /dev/null +++ b/layout/_partial/feed.ejs @@ -0,0 +1,12 @@ + +
+ <% page.posts.forEach(function(post) { %> + <%- include('post_feed', { post: post }) %> + <% }); %> +
+ + \ No newline at end of file diff --git a/layout/_partial/post_feed.ejs b/layout/_partial/post_feed.ejs new file mode 100644 index 0000000..fc98d36 --- /dev/null +++ b/layout/_partial/post_feed.ejs @@ -0,0 +1,32 @@ + +<% if(post.photos && post.photos.length) { %> + <% classTag = "post-with-image" %> +<% } else { %> + <% classTag = "post-without-image" %> +<% } %> + +
+

<%= post.title || "Untitled" %>

+ +
+ <% if(post.photos && post.photos.length) { %> + + <%- post.title || 'Untitled' %> + + <% } %> +
+ + + +

<%= post.description %>

+ +

Read More...

+ +
+ <% post.tags.data.forEach(function(tag) { %> + #<%= tag.name %> + <% }); %> +
+
\ No newline at end of file diff --git a/layout/_partial/scarves.ejs b/layout/_partial/scarves.ejs new file mode 100644 index 0000000..a3e2b55 --- /dev/null +++ b/layout/_partial/scarves.ejs @@ -0,0 +1,17 @@ +
+ <% theme.scarf.forEach(function(scarf, index) { %> + <% if (scarf.enable) { %> + +
+ <% if(scarf.image) { %> + + <% } else if(scarf.icon) { %> + + <% } else%> +
+
+ <% } %> + <% }); %> +
\ No newline at end of file diff --git a/layout/archive.ejs b/layout/archive.ejs new file mode 100644 index 0000000..1cbb80e --- /dev/null +++ b/layout/archive.ejs @@ -0,0 +1,9 @@ +<% site.tags.forEach(function(tag) { %> +

<%= tag.name %>

+ +<% }); %> + diff --git a/layout/category.ejs b/layout/category.ejs new file mode 100644 index 0000000..5a7de68 --- /dev/null +++ b/layout/category.ejs @@ -0,0 +1 @@ +<%- include("_partial/feed", { item: page.posts.sort('-date') }) %> \ No newline at end of file diff --git a/layout/index.ejs b/layout/index.ejs new file mode 100644 index 0000000..5b60c5a --- /dev/null +++ b/layout/index.ejs @@ -0,0 +1 @@ +<%- include("_partial/feed", { item: site.posts.sort('-date') }) %> \ No newline at end of file diff --git a/layout/layout.ejs b/layout/layout.ejs new file mode 100644 index 0000000..63cfe5a --- /dev/null +++ b/layout/layout.ejs @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + <%= config.title %> + + + + + +
+ <% if (theme.scarves) { %> + <%- include('_partial/scarves') %> + <% } %> + <% if (theme.sidebar) { %> + + <% } %> +
+ <%- body %> +
+
+ + diff --git a/layout/page.ejs b/layout/page.ejs new file mode 100644 index 0000000..e9bd95d --- /dev/null +++ b/layout/page.ejs @@ -0,0 +1,15 @@ + +
+
+

<%= page.title || "Untitled" %>

+ <% if(page.photos && page.photos.length) { %> +
+ <%- page.title || "> +
+ <% } %> +

<%= page.description %>

+
+
+ <%- page.content %> +
+
\ No newline at end of file diff --git a/layout/post.ejs b/layout/post.ejs new file mode 100644 index 0000000..b1bdf0a --- /dev/null +++ b/layout/post.ejs @@ -0,0 +1,27 @@ + +
+
+

<%= page.title || "Untitled" %>

+ <% if(page.photos && page.photos.length) { %> +
+ <%- page.title || "> +
+ <% } %> +

<%= page.description %>

+ +
+
+ <%- page.content %> +
+ +
\ No newline at end of file diff --git a/layout/tag.ejs b/layout/tag.ejs new file mode 100644 index 0000000..5a7de68 --- /dev/null +++ b/layout/tag.ejs @@ -0,0 +1 @@ +<%- include("_partial/feed", { item: page.posts.sort('-date') }) %> \ No newline at end of file diff --git a/source/css/style.css b/source/css/style.css new file mode 100644 index 0000000..d531425 --- /dev/null +++ b/source/css/style.css @@ -0,0 +1,299 @@ +body { + font-family: "Helvetica"; + margin: auto; + font-size: 14px; + background-color: #343a40; +} + +main{ + padding: 20px; +} + +h1, h2, h3, h4 { + font-weight: lighter; +} + +h1, h2 { + text-align: center; +} + +a { + text-decoration: none; + transition: color 1s; +} + +/* Definition of small fonted text, example: footer */ +.smoll { + font-size: 12px !important; + line-height: 12px; +} + +/* Sidebar header section */ +.sidebar { + margin-right: 1em; + margin-bottom: 0; + flex-shrink: 0; +} + +.avatar img { + border-radius: 100%; + width: 50%; + margin: 15px auto; + position: relative; + z-index: 2; +} + +/* Sidebar content section */ +.sidebar-content { + padding: 1em; + margin-top: 35px; + background-color: #dcdcdf; + color:#343a40; + border-radius: 10px; +} + +.sidebar-content p { + line-height: 17px; + font-size: 15px; +} + +.social { + justify-content: center; + margin-top: 0.5em; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} + +.social-icon a { + padding: 5px; + box-sizing: border-box; + display:inline-block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: 14px; + transition: color 0.3s; +} + +.social-icon img { + width: 15px !important; + height: 15px !important; + border-radius: 50%; +} + +/* Sidebar menu section */ +nav a { + margin: 0.75rem auto; + padding: 0.25rem 0; + display: block; + background-color: #343a40; + color: #dcdcdf !important; + width: 75%; + height: 25px; + text-align: center; + line-height: 25px; + border-radius: 50px; + transition: color 0.3s; +} + +/* Footer under the sidebar */ +.footer { + color: #dcdcdf !important; + text-align: center; + margin-top: 5px; +} + +.footer a { + color: #dcdcdf !important; +} + +/* Flags */ +.border{ + border-radius: 10px; +} + +/* Posts and pages */ +.post-feed{ + padding: 1em calc(1em + 10%); + margin: 0 auto 1em; + width: 60%; + align-items: center; + color: #dcdcdf; + background-color: #2e3237; +} + +.post-feed h1{ + color:#dcdcdf !important; +} + +.pp{ + padding: 1em calc(1em + 10%); + box-sizing: border-box; +} + +.pp h1{ + padding: 1em 0; +} + +.post{ + background-color: #dcdcdf; +} + +.page{ + color: #dcdcdf !important; +} + +.page-divider { + margin-top: 1em; + border-top: 1px solid #343a40; +} + +/* Images */ +img { + max-width: 100%; + height: auto; + display: block; + opacity: 1; + transition: all 1s; +} + +img:hover { + opacity: 0.95; +} + +iframe { + width: 100%; + height: auto; + aspect-ratio: 16 / 9; +} + +/* Date */ +.date { + display: flex; + } + +.date a{ + font-style: italic; + margin-left: auto; + text-align: right !important; + } + +.post .date a{ + color: #343a40 !important; +} + +.post-feed .date a { + margin-top:1em; + color: #9fa5aa !important; +} + +@media (max-width: 768px) { + .sidebar-content { + display: none; + } + + .post_description { + display: none; + } + + .footer { + display: none; + } + + .content { + width: calc(100% - 1em); + } +} + +/* Medium devices (tablets, 576px-768px) */ +@media (min-width: 577px) and (max-width: 768px) { + body{ + width: 576px; + } +} + +@media (min-width: 769px) { + .sidebar { + width: 250px; + height: auto; + position: fixed; + z-index: 1; + } + + .content { + width: calc(100% - 250px - 1em); + margin-left: calc(250px + 1em); + } + +} + +/* Large devices (desktops, 768px-992px) */ +@media (min-width: 769px) and (max-width: 992px) { + body{ + width: 769px; + } +} + +@media (min-width: 993px) and (max-width: 1199px) { + body{ + width: 993px; + } +} + +@media (min-width: 1200px) { + body{ + width: 1200px; + } +} + +/* Post - Tags */ +.tags { + text-align: right; + margin-bottom: 0em; +} + + +/* Scarves section */ +.scarves { + position:fixed; + right: 0; + margin-right:50px; + z-index: 10; +} + +.scarf { + width:25px; + height:100px; + position:absolute; + transition: all 1s, transform 1s; + -moz-transition: all 1s, -moz-transform 1s; + -webkit-transition: all 1s, -webkit-transform 1s; + -o-transition: all 1s, -o-transform 1s; + opacity:1; +} + +.scarf .logo { + width:21px; + bottom: 0px; + font-size: 15px; + text-align: center; + padding: 5px 2px; + position: absolute; + object-fit: cover; +} + +.scarf:hover { + margin-top:-25px !important; + transition: all 1s, transform 1s; + -moz-transition: all 1s, -moz-transform 1s; + -webkit-transition: all 1s, -webkit-transform 1s; + -o-transition: all 1s, -o-transform 1s; + opacity:0.9; +} + +.scarf:hover img { + opacity: 0.75; + transition: opacity 1s; +}