Personal Portfolio
JS-rendered personal profile card (J002 layout): avatar, social links, experience stats, contact buttons, Portfolio grid with tag filter and Skills year rings.
Overview
| Item | Value |
|---|---|
| Created | 2023-02-26 |
| Category | Personal Examples |
| HTML files | index.html |
| Source | target/20230226 |
Live Preview
Page Sections
| Section | Description |
|---|---|
| Top user card | setTopUser(): avatar (or name initial), name with title tooltip, LinkedIn / Instagram / Facebook / GitHub / blog links (nulls skipped), 3 experience stats, '查看原碼' main button and contact icons (email, Messenger). |
| Page tabs | ul#page-tab with Portfolio / Skills, selected state via sel attribute. |
| Portfolio (default, ?page=portfolio) | setPortfolio(): 20 sample figures with image, title, 開啟 link and 分享 button; floating ul#portfolio-tab filters ALL / tag 1 / tag 2. |
| Skills (?page=skills) | setSkills(): skill groups (iOS, Frontend, Backend, Database, Google Cloud Platform, OS, more) with per-item year rings, distributed into two masonry-like columns by comparing scrollHeight. |
Interactions
- All DOM built on DOMContentLoaded from webConfig with PDRenderKit helpers ("tag#id.class"._(), _child, _click, _go, _open, $timestamp.$gone for years since a date).
- #body-container scroll >= 40px adds 'show' to #portfolio-tab.
- Tabs on narrow screens (page tab < 640px, portfolio tab < 800px): first tap adds 'expand', second tap selects and re-renders the list.
- copy(url): temporary readonly input + document.execCommand('copy') then alert('已拷貝至剪貼簿.').
- _LazyListener() (PDRenderKit) is started for image lazy loading.
Dependencies
| Name | Version | Source |
|---|---|---|
| PDRenderKit | 1.0.0 (script tag; preload tag requests 1.0.2) | https://cdn.jsdelivr.net/npm/pdrenderkit@1.0.0/dist/PDRenderKit.js |
| Font Awesome Free | 6.5.2 | https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/css/all.min.css |
| Google Analytics gtag | - | https://www.googletagmanager.com/gtag/js?id=G-L5VYEZPVXX |
| js/index.js (local app code with webConfig sample data) | - | js/index.js |
Styling
| Item | Value |
|---|---|
| SCSS entry | sass/index.scss |
| Output CSS | css/index.css |
| Partials | J002/_nav.scss, J002/_div.scss, J002/_portfolio-tab.scss, J002/_portfolio-list.scss, J002/_skill-list.scss, J002/_top-user.scss, J002/top-user/_head.scss, J002/top-user/_name.scss, J002/top-user/_link.scss, J002/top-user/_exp.scss, J002/top-user/_button.scss, J002/top-user/_tab.scss |
Layout namespaced under J002; top-user split into head / name / link / exp / button / tab partials. All colours, sizes and gaps come from CSS variables declared in index.html.
cd target/20230226
npx sass sass/index.scss css/index.css --no-source-map || exit 1
Techniques
- Extensive CSS-variable theming in inline <style>: --global-bg, --user-top-, --portfolio-, --skill-* (gradients, sizes, gaps, ratio).
- Skill year rings: Sass @for 1..10 generates [year="N"] rules rotating two half-circle layers by 36deg per year.
- content: attr(num) / attr(title) for stats and tooltips; [num="N"] and [sel="N"] attribute selectors generated with @for.
- Mobile-first breakpoints 320 / 480 / 640 / 800 / 1024px and @media (hover: hover).
External Assets
- https://pardn.io/web-template/target/20230226/image/bg.jpg (--global-bg, absolute URL)
https://fakeimg.pl/1024/282828/eae0d0/?text=samplehttps://pardn.io/image/head.pnghttps://pardn.io/web-template/image/20230226.jpg
Known Issues
- --global-bg is hard-coded to https://pardn.io/web-template/target/20230226/image/bg.jpg, so the copied page still loads the background from pardn.io instead of its own image/bg.jpg; it breaks if that remote file is unavailable.