Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/CaiJimmy/hugo-theme-stack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJimmy Cai <jimmehcai@gmail.com>2020-08-22 14:20:08 +0300
committerJimmy Cai <jimmehcai@gmail.com>2020-08-22 14:20:08 +0300
commitc698d944e6b18b4707d96385df9aa0b550d2ec81 (patch)
treeefa66b831e4134092e13677b34775c3cc2dc30a6
:tada: Initial commit
-rw-r--r--LICENSE674
-rw-r--r--archetypes/default.md2
-rw-r--r--assets/icons/archive.svg8
-rw-r--r--assets/icons/arrow-back.svg6
-rw-r--r--assets/icons/back.svg6
-rw-r--r--assets/icons/chevrons-left.svg7
-rw-r--r--assets/icons/chevrons-right.svg7
-rw-r--r--assets/icons/clock.svg7
-rw-r--r--assets/icons/copyright.svg7
-rw-r--r--assets/icons/hash.svg9
-rw-r--r--assets/icons/home.svg8
-rw-r--r--assets/icons/infinity.svg6
-rw-r--r--assets/icons/link.svg7
-rw-r--r--assets/icons/messages.svg7
-rw-r--r--assets/icons/rss.svg8
-rw-r--r--assets/icons/tag.svg7
-rw-r--r--assets/icons/user.svg7
-rw-r--r--assets/scss/external/normalize.scss349
-rw-r--r--assets/scss/helper/shadow.scss12
-rw-r--r--assets/scss/partials/article.scss254
-rw-r--r--assets/scss/partials/base.scss17
-rw-r--r--assets/scss/partials/footer.scss30
-rw-r--r--assets/scss/partials/layout/404.scss6
-rw-r--r--assets/scss/partials/layout/archive.scss32
-rw-r--r--assets/scss/partials/layout/article.scss303
-rw-r--r--assets/scss/partials/layout/taxonomy.scss56
-rw-r--r--assets/scss/partials/menu.scss214
-rw-r--r--assets/scss/partials/pagination.scss21
-rw-r--r--assets/scss/partials/sidebar.scss150
-rw-r--r--assets/scss/partials/widgets.scss79
-rw-r--r--assets/scss/style.scss131
-rw-r--r--assets/scss/variables.scss92
-rw-r--r--assets/ts/color.ts49
-rw-r--r--assets/ts/gallery.ts287
-rw-r--r--assets/ts/main.ts96
-rw-r--r--assets/ts/menu.ts83
-rw-r--r--assets/ts/utils.ts43
-rw-r--r--layouts/404.html13
-rw-r--r--layouts/_default/_markup/render-image.html18
-rw-r--r--layouts/_default/baseof.html11
-rw-r--r--layouts/_default/single.html30
-rw-r--r--layouts/_default/term.html41
-rw-r--r--layouts/index.html25
-rw-r--r--layouts/page/archive.html39
-rw-r--r--layouts/page/single.html18
-rw-r--r--layouts/partials/article-list/compact.html24
-rw-r--r--layouts/partials/article-list/default.html51
-rw-r--r--layouts/partials/article-list/tile.html17
-rw-r--r--layouts/partials/article/article.html75
-rw-r--r--layouts/partials/article/components/comment.html1
-rw-r--r--layouts/partials/article/components/photoswipe.html66
-rw-r--r--layouts/partials/article/components/related-contents.html13
-rw-r--r--layouts/partials/data/description.html11
-rw-r--r--layouts/partials/data/title.html16
-rw-r--r--layouts/partials/footer.html7
-rw-r--r--layouts/partials/footer/script.html15
-rw-r--r--layouts/partials/footer/style.html0
-rw-r--r--layouts/partials/head.html18
-rw-r--r--layouts/partials/head/opengraph.html51
-rw-r--r--layouts/partials/head/script.html0
-rw-r--r--layouts/partials/head/style.html5
-rw-r--r--layouts/partials/helper/image.html2
-rw-r--r--layouts/partials/pagination.html26
-rw-r--r--layouts/partials/sidebar/left.html33
-rw-r--r--layouts/partials/sidebar/right.html8
-rw-r--r--layouts/partials/widget/archive.html19
-rw-r--r--layouts/partials/widget/tag-cloud.html17
-rw-r--r--layouts/shortcodes/youtube.html9
-rw-r--r--theme.toml15
69 files changed, 3781 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e72bfdd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>. \ No newline at end of file
diff --git a/archetypes/default.md b/archetypes/default.md
new file mode 100644
index 0000000..ac36e06
--- /dev/null
+++ b/archetypes/default.md
@@ -0,0 +1,2 @@
++++
++++
diff --git a/assets/icons/archive.svg b/assets/icons/archive.svg
new file mode 100644
index 0000000..cd96cbe
--- /dev/null
+++ b/assets/icons/archive.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-archive" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <rect x="3" y="4" width="18" height="4" rx="2" />
+ <path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10" />
+ <line x1="10" y1="12" x2="14" y2="12" />
+</svg>
+
+
diff --git a/assets/icons/arrow-back.svg b/assets/icons/arrow-back.svg
new file mode 100644
index 0000000..0f7c5f4
--- /dev/null
+++ b/assets/icons/arrow-back.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-back" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <path d="M9 11l-4 4l4 4m-4 -4h11a4 4 0 0 0 0 -8h-1" />
+</svg>
+
+
diff --git a/assets/icons/back.svg b/assets/icons/back.svg
new file mode 100644
index 0000000..ee52db4
--- /dev/null
+++ b/assets/icons/back.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <polyline points="15 6 9 12 15 18" />
+</svg>
+
+
diff --git a/assets/icons/chevrons-left.svg b/assets/icons/chevrons-left.svg
new file mode 100644
index 0000000..494e3ce
--- /dev/null
+++ b/assets/icons/chevrons-left.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevrons-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <polyline points="11 7 6 12 11 17" />
+ <polyline points="17 7 12 12 17 17" />
+</svg>
+
+
diff --git a/assets/icons/chevrons-right.svg b/assets/icons/chevrons-right.svg
new file mode 100644
index 0000000..a54255b
--- /dev/null
+++ b/assets/icons/chevrons-right.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevrons-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <polyline points="7 7 12 12 7 17" />
+ <polyline points="13 7 18 12 13 17" />
+</svg>
+
+
diff --git a/assets/icons/clock.svg b/assets/icons/clock.svg
new file mode 100644
index 0000000..a7a8be6
--- /dev/null
+++ b/assets/icons/clock.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-clock" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <circle cx="12" cy="12" r="9" />
+ <polyline points="12 7 12 12 15 15" />
+</svg>
+
+
diff --git a/assets/icons/copyright.svg b/assets/icons/copyright.svg
new file mode 100644
index 0000000..2ac45ff
--- /dev/null
+++ b/assets/icons/copyright.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copyright" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <circle cx="12" cy="12" r="9" />
+ <path d="M14.5 9a3.5 4 0 1 0 0 6" />
+</svg>
+
+
diff --git a/assets/icons/hash.svg b/assets/icons/hash.svg
new file mode 100644
index 0000000..e00ab1d
--- /dev/null
+++ b/assets/icons/hash.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-hash" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <line x1="5" y1="9" x2="19" y2="9" />
+ <line x1="5" y1="15" x2="19" y2="15" />
+ <line x1="11" y1="4" x2="7" y2="20" />
+ <line x1="17" y1="4" x2="13" y2="20" />
+</svg>
+
+
diff --git a/assets/icons/home.svg b/assets/icons/home.svg
new file mode 100644
index 0000000..ed5fd53
--- /dev/null
+++ b/assets/icons/home.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-home" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <polyline points="5 12 3 12 12 3 21 12 19 12" />
+ <path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7" />
+ <path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6" />
+</svg>
+
+
diff --git a/assets/icons/infinity.svg b/assets/icons/infinity.svg
new file mode 100644
index 0000000..fb40b24
--- /dev/null
+++ b/assets/icons/infinity.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-infinity" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <path d="M9.828 9.172a4 4 0 1 0 0 5.656 a10 10 0 0 0 2.172 -2.828a10 10 0 0 1 2.172 -2.828 a4 4 0 1 1 0 5.656a10 10 0 0 1 -2.172 -2.828a10 10 0 0 0 -2.172 -2.828" />
+</svg>
+
+
diff --git a/assets/icons/link.svg b/assets/icons/link.svg
new file mode 100644
index 0000000..3c87803
--- /dev/null
+++ b/assets/icons/link.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-link" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5" />
+ <path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5" />
+</svg>
+
+
diff --git a/assets/icons/messages.svg b/assets/icons/messages.svg
new file mode 100644
index 0000000..725d75c
--- /dev/null
+++ b/assets/icons/messages.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-messages" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <path d="M21 14l-3 -3h-7a1 1 0 0 1 -1 -1v-6a1 1 0 0 1 1 -1h9a1 1 0 0 1 1 1v10" />
+ <path d="M14 15v2a1 1 0 0 1 -1 1h-7l-3 3v-10a1 1 0 0 1 1 -1h2" />
+</svg>
+
+
diff --git a/assets/icons/rss.svg b/assets/icons/rss.svg
new file mode 100644
index 0000000..b92ea8f
--- /dev/null
+++ b/assets/icons/rss.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-rss" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <circle cx="5" cy="19" r="1" />
+ <path d="M4 4a16 16 0 0 1 16 16" />
+ <path d="M4 11a9 9 0 0 1 9 9" />
+</svg>
+
+
diff --git a/assets/icons/tag.svg b/assets/icons/tag.svg
new file mode 100644
index 0000000..ef7a63d
--- /dev/null
+++ b/assets/icons/tag.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-tag" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <path d="M11 3L20 12a1.5 1.5 0 0 1 0 2L14 20a1.5 1.5 0 0 1 -2 0L3 11v-4a4 4 0 0 1 4 -4h4" />
+ <circle cx="9" cy="9" r="2" />
+</svg>
+
+
diff --git a/assets/icons/user.svg b/assets/icons/user.svg
new file mode 100644
index 0000000..9c92c87
--- /dev/null
+++ b/assets/icons/user.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z"/>
+ <circle cx="12" cy="7" r="4" />
+ <path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" />
+</svg>
+
+
diff --git a/assets/scss/external/normalize.scss b/assets/scss/external/normalize.scss
new file mode 100644
index 0000000..c45a85f
--- /dev/null
+++ b/assets/scss/external/normalize.scss
@@ -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;
+ } \ No newline at end of file
diff --git a/assets/scss/helper/shadow.scss b/assets/scss/helper/shadow.scss
new file mode 100644
index 0000000..ad326f9
--- /dev/null
+++ b/assets/scss/helper/shadow.scss
@@ -0,0 +1,12 @@
+@mixin box_shadow($level) {
+ @if $level == 1 {
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.06), 0px 0px 1px rgba(0, 0, 0, 0.04);
+ } @else if $level == 2 {
+ box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
+ } @else if $level == 3 {
+ box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.04), 0px 2px 6px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
+ } @else if $level == 4 {
+ box-shadow: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
+ 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04);
+ }
+}
diff --git a/assets/scss/partials/article.scss b/assets/scss/partials/article.scss
new file mode 100644
index 0000000..5fffcb9
--- /dev/null
+++ b/assets/scss/partials/article.scss
@@ -0,0 +1,254 @@
+/* Default article style */
+.article-list {
+ display: flex;
+ flex-direction: column;
+
+ article {
+ display: flex;
+ flex-direction: column;
+
+ margin-bottom: 40px;
+ background-color: var(--card-background);
+ @include box_shadow(1);
+ border-radius: var(--card-border-radius);
+ overflow: hidden;
+
+ .article-image {
+ img {
+ width: 100%;
+ height: 250px;
+ object-fit: cover;
+
+ @media (max-width: $on-tablet) {
+ height: 150px;
+ }
+
+ @media (max-width: $on-desktop) {
+ height: 200px;
+ }
+ }
+ }
+
+ @for $i from 1 through length($defaultTagBackgrounds) {
+ &:nth-child(#{length($defaultTagBackgrounds)}n + #{$i}) {
+ .article-category a {
+ background: nth($defaultTagBackgrounds, $i);
+ color: nth($defaultTagColors, $i);
+ }
+ }
+ }
+ }
+}
+
+.article-details {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ padding: 25px 20px;
+
+ @media (max-width: $on-desktop) {
+ padding: 20px;
+ }
+}
+
+.article-category {
+ margin-bottom: 10px;
+ a {
+ display: inline-block;
+ text-decoration: none;
+ padding: 8px 16px;
+ font-size: 1.4rem;
+ background: nth($defaultTagBackgrounds, 1);
+ color: nth($defaultTagColors, 1);
+ border-radius: var(--tag-border-radius);
+
+ @media (max-width: $on-phone) {
+ font-size: 1.25rem;
+ padding: 8px 14px;
+ }
+ }
+}
+
+.article-title {
+ font-size: 2.4rem;
+ font-weight: 600;
+ margin: 10px 0;
+
+ a {
+ color: var(--card-text-color-main);
+
+ &:hover {
+ color: var(--card-text-color-main);
+ }
+ }
+
+ @media (max-width: $on-desktop) {
+ font-size: 1.5em;
+ }
+}
+
+.article-subtitle {
+ font-weight: lighter;
+ font-size: 2rem;
+ color: var(--card-text-color-secondary);
+ margin: 5px 0;
+ line-height: 1.5;
+ @media (max-width: $on-desktop) {
+ font-size: 1.8rem;
+ }
+}
+
+.article-time {
+ display: flex;
+ align-items: center;
+ color: var(--card-text-color-tertiary);
+ margin-top: 10px;
+
+ svg {
+ vertical-align: middle;
+ margin-right: 8px;
+ }
+
+ time {
+ font-size: 16px;
+ @media (max-width: $on-desktop) {
+ font-size: 14px;
+ }
+ }
+}
+
+.article-tags {
+ & + .article-copyright {
+ margin-top: 30px;
+ }
+
+ a {
+ color: var(--accent-color-text);
+ background-color: var(--accent-color);
+ padding: 8px 18px;
+ border-radius: var(--tag-border-radius);
+ display: inline-block;
+ font-size: 1.4rem;
+ margin-right: 10px;
+ margin-bottom: 10px;
+ transition: background-color 0.5s ease;
+
+ &:hover {
+ color: var(--accent-color-text);
+ background-color: var(--accent-color-darker);
+ }
+ }
+}
+
+/* Compact style article list */
+.article-list--compact {
+ border-radius: var(--card-border-radius);
+ @include box_shadow(1);
+ background-color: var(--card-background);
+ padding: 0 15px;
+
+ & + .pagination {
+ margin-top: 40px;
+ }
+
+ article {
+ display: flex;
+ align-items: center;
+ padding: 25px 10px;
+
+ &:not(:last-of-type) {
+ border-bottom: 2px solid var(--card-separator-color);
+ }
+
+ .article-details {
+ flex-grow: 1;
+ padding: 0;
+ padding-right: 15px;
+ }
+
+ .article-title {
+ margin: 0;
+ font-size: 2rem;
+
+ @media (max-width: $on-phone) {
+ font-size: 1.6rem;
+ }
+ }
+
+ .article-image {
+ img {
+ width: 60px;
+ height: 60px;
+ }
+ }
+ }
+}
+
+/* Tile style article list */
+.article-list--tile {
+ article {
+ border-radius: var(--card-border-radius);
+ overflow: hidden;
+ position: relative;
+ height: 350px;
+ width: 250px;
+ @include box_shadow(1);
+ transition: box-shadow 0.3s ease;
+ background-color: var(--card-background);
+
+ &:hover {
+ @include box_shadow(2);
+ }
+
+ &.has-image {
+ .article-details {
+ background-color: rgba(#000, 0.25);
+ }
+
+ .article-title {
+ color: #fff;
+ }
+ }
+
+ .article-image {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+
+ .article-details {
+ border-radius: var(--card-border-radius);
+ position: relative;
+ height: 100%;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ z-index: 2;
+ padding: 20px;
+
+ @media (max-width: $on-phone) {
+ padding: 15px;
+ }
+ }
+
+ .article-title {
+ font-size: 2.2rem;
+ font-weight: 500;
+ color: var(--card-text-color-main);
+
+ @media (max-width: $on-phone) {
+ font-size: 2rem;
+ }
+ }
+ }
+}
diff --git a/assets/scss/partials/base.scss b/assets/scss/partials/base.scss
new file mode 100644
index 0000000..27f15e9
--- /dev/null
+++ b/assets/scss/partials/base.scss
@@ -0,0 +1,17 @@
+html {
+ font-size: 62.5%;
+ font-display: swap;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ background: var(--body-background);
+ margin: 0;
+ font-family: $base-font-family;
+ font-size: 1.6rem;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
diff --git a/assets/scss/partials/footer.scss b/assets/scss/partials/footer.scss
new file mode 100644
index 0000000..ca55f49
--- /dev/null
+++ b/assets/scss/partials/footer.scss
@@ -0,0 +1,30 @@
+footer.site-footer {
+ padding: 20px 0 40px 0;
+ font-size: 1.4rem;
+ line-height: 1.75;
+
+ &:before {
+ content: "";
+ display: block;
+ height: 3px;
+ width: 50px;
+ background: var(--body-text-color);
+ margin-bottom: 20px;
+ }
+
+ .copyright {
+ color: var(--accent-color);
+ font-weight: bold;
+ margin-bottom: 5px;
+ }
+
+ .powerby {
+ color: var(--body-text-color);
+ font-weight: normal;
+ font-size: 1.2rem;
+
+ a {
+ color: var(--body-text-color);
+ }
+ }
+}
diff --git a/assets/scss/partials/layout/404.scss b/assets/scss/partials/layout/404.scss
new file mode 100644
index 0000000..925aa59
--- /dev/null
+++ b/assets/scss/partials/layout/404.scss
@@ -0,0 +1,6 @@
+.not-found-card {
+ background-color: var(--card-background);
+ @include box_shadow(1);
+ border-radius: var(--card-border-radius);
+ padding: 30px;
+}
diff --git a/assets/scss/partials/layout/archive.scss b/assets/scss/partials/layout/archive.scss
new file mode 100644
index 0000000..7d62d18
--- /dev/null
+++ b/assets/scss/partials/layout/archive.scss
@@ -0,0 +1,32 @@
+.archive-group {
+ margin-bottom: 40px;
+ .archive-date {
+ text-transform: uppercase;
+ margin-bottom: 10px;
+ font-size: 1.6rem;
+ font-weight: 500;
+
+ a {
+ color: var(--body-text-color);
+ }
+ }
+}
+
+.template-archive {
+ .category-list {
+ margin-bottom: 40px;
+ overflow-x: auto;
+
+ .article-list--tile {
+ display: flex;
+ padding-bottom: 15px;
+
+ article {
+ width: 250px;
+ height: 150px;
+ margin-right: 20px;
+ flex-shrink: 0;
+ }
+ }
+ }
+}
diff --git a/assets/scss/partials/layout/article.scss b/assets/scss/partials/layout/article.scss
new file mode 100644
index 0000000..0c15121
--- /dev/null
+++ b/assets/scss/partials/layout/article.scss
@@ -0,0 +1,303 @@
+@import "variables.scss";
+
+.article-page {
+ .left-sidebar {
+ @media (max-width: $on-tablet) {
+ display: none;
+ }
+ }
+
+ .article-sidebar {
+ position: sticky;
+ top: 50px;
+ flex-shrink: 0;
+
+ @media (max-width: $on-tablet) {
+ display: none;
+ }
+
+ @media (min-width: $on-tablet) {
+ padding-left: 15px;
+ margin-left: 1%;
+ }
+
+ @media (min-width: $on-tablet) {
+ width: 25%;
+ margin-right: 1%;
+ }
+
+ @media (min-width: $on-desktop) {
+ width: 30%;
+ }
+ }
+
+ article {
+ background: var(--card-background);
+ border-radius: var(--card-border-radius);
+ @include box_shadow(1);
+ overflow: hidden;
+
+ &.main-article {
+ margin-bottom: 40px;
+ }
+
+ .article-header {
+ .article-image {
+ img {
+ height: auto;
+ width: 100%;
+ max-height: 500px;
+ width: 100%;
+ object-fit: cover;
+ }
+ }
+
+ .article-details {
+ padding: 30px 30px 0 30px;
+ @media (max-width: $on-tablet) {
+ padding: 20px 20px 0 20px;
+ }
+ }
+ }
+
+ .article-content {
+ margin: 30px 0;
+ padding: 0 30px;
+ line-height: 1.75;
+ color: var(--card-text-color-main);
+
+ @media (max-width: $on-tablet) {
+ padding: 0 20px;
+ font-size: 1.6rem;
+ }
+
+ img {
+ max-width: 100%;
+ height: auto;
+ }
+ }
+
+ .article-footer {
+ padding: 30px;
+
+ @media (max-width: $on-phone) {
+ padding: 20px;
+ }
+
+ .article-copyright {
+ color: var(--card-text-color-tertiary);
+ text-transform: uppercase;
+ display: flex;
+ align-items: center;
+
+
+ svg {
+ margin-right: 15px;
+ stroke-width: 1.33;
+ }
+ }
+ }
+ }
+}
+
+#TableOfContents {
+ background: var(--card-background);
+ border-radius: var(--card-border-radius);
+ @include box_shadow(1);
+ ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+
+ li {
+ padding: 15px 30px;
+ padding-right: 50px;
+ position: relative;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid var(--card-separator-color);
+ }
+
+ a {
+ color: var(--accent-color);
+ }
+
+ ul {
+ border-top: 1px solid var(--card-separator-color);
+ margin-top: 15px;
+ }
+ }
+ }
+}
+
+#article-toolbar {
+ background: var(--card-background);
+ @include box_shadow(1);
+ border-radius: var(--card-border-radius);
+ margin-bottom: 20px;
+ padding: 15px 20px;
+ display: flex;
+ align-items: center;
+
+ @media (max-width: $on-phone) {
+ margin-top: 15px;
+ }
+
+ @media (min-width: $on-tablet) {
+ display: none;
+ }
+
+ .back-home {
+ color: var(--card-text-color-secondary);
+ margin-right: 30px;
+ display: inline-flex;
+ align-items: center;
+
+ svg {
+ margin-right: 5px;
+ }
+
+ span {
+ font-weight: 500;
+ }
+ }
+}
+
+.related-contents--wrapper {
+ margin-bottom: 40px;
+ .widget-title {
+ //padding-left: 15px;
+ }
+}
+
+.related-contents {
+ overflow-x: auto;
+ //padding: 0 15px;
+ padding-bottom: 15px;
+
+ & > .flex {
+ float: left;
+ }
+
+ article {
+ margin-right: 15px;
+ flex-shrink: 0;
+ overflow: hidden;
+
+ @media (max-width: $on-phone) {
+ height: 250px;
+ width: 200px;
+ }
+
+ @media (min-width: $on-phone) {
+ height: 200px;
+ width: 300px;
+ }
+
+ .article-title {
+ font-size: 1.8rem;
+ }
+
+ .article-details {
+ transition: background-color 0.5s ease;
+ }
+ }
+}
+
+.article-content {
+ font-family: -apple-system, BlinkMacSystemFont, "noto-sans", "Segoe UI", "Droid Sans", "Helvetica Neue",
+ "PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", sans-serif;
+
+ p {
+ margin: 1.5em 0;
+ }
+
+ figure {
+ margin: 0 auto;
+ text-align: center;
+
+ figcaption {
+ font-size: 1.6rem;
+ }
+ }
+
+ blockquote {
+ position: relative;
+ margin: 10px 0;
+ border-left: 4px solid var(--card-separator-color);
+ padding-left: 20px;
+ }
+
+ hr {
+ width: 100px;
+ margin: 40px 0;
+ background: var(--card-text-color-tertiary);
+ height: 4px;
+ border: 0;
+ }
+
+ code {
+ color: #808080;
+ font-size: 0.96em;
+ background-color: #f9f9f7;
+ padding: 1px 2px;
+ border: 1px solid #eee;
+ border-radius: 3px;
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
+ word-wrap: break-word;
+ }
+
+ .gallery {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ margin: 20px 0;
+
+ figure {
+ margin: 0 5px;
+
+ a:after {
+ display: none;
+ }
+
+ img {
+ //width: 100%;
+ }
+ }
+ }
+
+ pre {
+ overflow-x: auto;
+ display: block;
+ background-color: #fafafa;
+ color: #999;
+ font-family: Consolas, monospace;
+ line-height: 1.428571429;
+ word-break: break-all;
+ padding: 20px;
+ border-radius: var(--card-border-radius);
+
+ code {
+ color: unset;
+ border: none;
+ background: none;
+ padding: 0;
+ }
+ }
+
+ a {
+ position: relative;
+ &:after {
+ content: "";
+ display: block;
+ position: absolute;
+ bottom: -3px;
+ left: 0;
+ width: 100%;
+ background-color: var(--accent-color);
+ opacity: 0.2;
+ height: 3px;
+ }
+ }
+}
diff --git a/assets/scss/partials/layout/taxonomy.scss b/assets/scss/partials/layout/taxonomy.scss
new file mode 100644
index 0000000..01f4dcc
--- /dev/null
+++ b/assets/scss/partials/layout/taxonomy.scss
@@ -0,0 +1,56 @@
+.taxonomy-type {
+ text-transform: uppercase;
+ color: var(--body-text-color);
+ font-weight: 500;
+ margin-bottom: 5px;
+ font-size: 1.6rem;
+}
+
+.taxonomy-card {
+ border-radius: var(--card-border-radius);
+ background-color: var(--card-background);
+ padding: 20px 20px;
+ @include box_shadow(1);
+ margin-bottom: 40px;
+ display: flex;
+ align-items: center;
+
+ .taxonomy-term {
+ font-size: 2.2rem;
+ margin: 0;
+ margin-top: 15px;
+ color: var(--card-text-color-main);
+
+ & + .taxonomy-description {
+ margin-top: 15px;
+ }
+ }
+
+ .taxonomy-description {
+ font-weight: normal;
+ color: var(--card-text-color-secondary);
+ font-size: 1.8rem;
+ margin: 0;
+ }
+
+ .taxonomy-details {
+ flex-grow: 1;
+ padding: 10px 0;
+ margin-right: 20px;
+ }
+
+ .taxonomy-image {
+ img {
+ width: 60px;
+ height: 60px;
+ }
+ }
+
+ .taxonomy-count {
+ color: var(--card-text-color-tertiary);
+ font-size: 1.4rem;
+ margin: 0;
+ font-weight: 500;
+ text-transform: uppercase;
+ }
+}
diff --git a/assets/scss/partials/menu.scss b/assets/scss/partials/menu.scss
new file mode 100644
index 0000000..1d31831
--- /dev/null
+++ b/assets/scss/partials/menu.scss
@@ -0,0 +1,214 @@
+/*!
+ * Hamburgers
+ * @description Tasty CSS-animated hamburgers
+ * @author Jonathan Suh @jonsuh
+ * @site https://jonsuh.com/hamburgers
+ * @link https://github.com/jonsuh/hamburgers
+ */
+
+.hamburger {
+ padding-top: 10px;
+ display: inline-block;
+ cursor: pointer;
+ transition-property: opacity, filter;
+ transition-duration: 0.15s;
+ transition-timing-function: linear;
+ font: inherit;
+ color: inherit;
+ text-transform: none;
+ background-color: transparent;
+ border: 0;
+ margin: 0;
+ overflow: visible;
+}
+.hamburger:hover {
+ opacity: 0.7;
+}
+.hamburger.is-active:hover {
+ opacity: 0.7;
+}
+.hamburger.is-active .hamburger-inner,
+.hamburger.is-active .hamburger-inner::before,
+.hamburger.is-active .hamburger-inner::after {
+ background-color: #000;
+}
+
+.hamburger-box {
+ width: 30px;
+ height: 24px;
+ display: inline-block;
+ position: relative;
+}
+
+.hamburger-inner {
+ display: block;
+ top: 50%;
+ margin-top: -2px;
+}
+
+.hamburger-inner,
+.hamburger-inner::before,
+.hamburger-inner::after {
+ width: 30px;
+ height: 2px;
+ background-color: var(--card-text-color-main);
+ border-radius: 4px;
+ position: absolute;
+ transition-property: transform;
+ transition-duration: 0.15s;
+ transition-timing-function: ease;
+}
+.hamburger-inner::before,
+.hamburger-inner::after {
+ content: "";
+ display: block;
+}
+.hamburger-inner::before {
+ top: -10px;
+}
+.hamburger-inner::after {
+ bottom: -10px;
+}
+
+.hamburger--spin .hamburger-inner {
+ transition-duration: 0.22s;
+ transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
+}
+.hamburger--spin .hamburger-inner::before {
+ transition: top 0.1s 0.25s ease-in, opacity 0.1s ease-in;
+}
+.hamburger--spin .hamburger-inner::after {
+ transition: bottom 0.1s 0.25s ease-in, transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19);
+}
+
+.hamburger--spin.is-active .hamburger-inner {
+ transform: rotate(225deg);
+ transition-delay: 0.12s;
+ transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+.hamburger--spin.is-active .hamburger-inner::before {
+ top: 0;
+ opacity: 0;
+ transition: top 0.1s ease-out, opacity 0.1s 0.12s ease-out;
+}
+.hamburger--spin.is-active .hamburger-inner::after {
+ bottom: 0;
+ transform: rotate(-90deg);
+ transition: bottom 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1);
+}
+
+#toggle-menu {
+ background: none;
+ border: none;
+ position: absolute;
+ right: 30px;
+ top: 30px;
+ z-index: 2;
+ cursor: pointer;
+
+ @media (min-width: $on-phone + 1) {
+ display: none;
+ }
+ outline: none;
+
+ &.is-active {
+ .hamburger-inner,
+ .hamburger-inner::before,
+ .hamburger-inner::after {
+ background-color: var(--accent-color);
+ }
+ }
+}
+
+/* Menu style */
+.menu {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ margin: 30px 0;
+
+ @media (max-width: $on-phone) {
+ background-color: var(--card-background);
+ margin-top: 0;
+ padding: 15px 0;
+ @include box_shadow(1);
+ display: none;
+
+ &.show {
+ display: block;
+ }
+ }
+
+ @media (min-width: $on-phone + 1) {
+ align-items: flex-end;
+ }
+
+ li {
+ position: relative;
+ vertical-align: middle;
+ padding: 10px 0;
+
+ &:not(:last-of-type) {
+ margin-bottom: 20px;
+
+ @media (max-width: $on-desktop) {
+ margin-bottom: 10px;
+ }
+ }
+
+ @media (max-width: $on-phone) {
+ padding: 10px 30px;
+ }
+
+ @media (min-width: $on-phone + 1) and (max-width: ($on-desktop - 1)) {
+ padding: 10px 0;
+ }
+
+ @media (min-width: $on-phone + 1) {
+ width: 100%;
+ }
+
+ svg {
+ width: 32px;
+ height: 32px;
+ stroke-width: 1.33;
+
+ margin-right: 40px;
+
+ @media (max-width: $on-desktop) {
+ width: 30px;
+ height: 30px;
+ }
+ }
+
+ a {
+ height: 100%;
+ display: inline-flex;
+ align-items: center;
+ color: var(--body-text-color);
+
+ @media (max-width: $on-desktop) {
+ font-size: 1.5rem;
+ }
+ }
+
+ span {
+ flex: 1;
+ }
+
+ &.current {
+ a {
+ color: var(--accent-color);
+ font-weight: bold;
+ }
+ }
+ }
+}
+
+@media (max-width: $on-phone) {
+ .show-menu {
+ .site-info {
+ //@include box_shadow(2);
+ }
+ }
+}
diff --git a/assets/scss/partials/pagination.scss b/assets/scss/partials/pagination.scss
new file mode 100644
index 0000000..d1a5677
--- /dev/null
+++ b/assets/scss/partials/pagination.scss
@@ -0,0 +1,21 @@
+.pagination {
+ display: flex;
+ background-color: var(--card-background);
+ @include box_shadow(1);
+ border-radius: var(--card-border-radius);
+ overflow: hidden;
+ flex-wrap: wrap;
+ margin-bottom: 40px;
+ .page-link {
+ padding: 16px 32px;
+ display: inline-flex;
+
+ &.current {
+ font-weight: bold;
+ background-color: var(--card-background-selected);
+ color: var(--card-text-color-main);
+ }
+
+ color: var(--card-text-color-secondary);
+ }
+}
diff --git a/assets/scss/partials/sidebar.scss b/assets/scss/partials/sidebar.scss
new file mode 100644
index 0000000..9d229d4
--- /dev/null
+++ b/assets/scss/partials/sidebar.scss
@@ -0,0 +1,150 @@
+.sidebar {
+ padding: 0 15px;
+ &.sticky {
+ @media (min-width: ($on-phone + 1)) {
+ position: sticky;
+ }
+ }
+}
+
+.left-sidebar {
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+
+ @media (max-width: $on-phone) {
+ width: 100%;
+ padding: 30px 0;
+ max-width: none;
+ }
+
+ &.sticky {
+ top: 30px;
+ }
+
+ @media (min-width: $on-phone + 1) {
+ //justify-content: right;
+ //text-align: right;
+ width: 25%;
+ margin-right: 1%;
+ }
+
+ @media (min-width: $on-tablet) {
+ width: 25%;
+ margin-right: 1%;
+ }
+
+ @media (min-width: $on-desktop) {
+ width: 20%;
+ }
+
+ @media (min-width: $on-desktop-large) {
+ width: 15%;
+ }
+}
+
+.right-sidebar {
+ flex-shrink: 0;
+
+ &.sticky {
+ top: 30px;
+ }
+
+ @media (max-width: $on-desktop - 1) {
+ display: none;
+ }
+
+ @media (min-width: $on-tablet) {
+ width: 25%;
+ margin-left: 1%;
+ }
+
+ @media (min-width: $on-desktop + 1) {
+ width: 25%;
+ }
+}
+
+.site-info {
+ z-index: 1;
+ transition: box-shadow 0.5s ease;
+ @media (max-width: $on-phone) {
+ padding: 15px 30px;
+ }
+
+ .site-avatar {
+ position: relative;
+ margin: 0;
+ margin-bottom: 30px;
+ width: 150px;
+ height: 150px;
+
+ @media (min-width: $on-phone + 1) {
+ //margin-left: auto;
+ }
+
+ @media (max-width: $on-desktop) {
+ height: 120px;
+ width: 120px;
+ }
+
+ .site-logo {
+ width: 100%;
+ height: 100%;
+ border-radius: 100%;
+ @include box_shadow(1);
+ }
+
+ .emoji {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ line-height: 50px;
+ border-radius: 100%;
+ bottom: 0;
+ right: 0;
+ text-align: center;
+ font-size: 20px;
+ background-color: var(--card-background);
+ @include box_shadow(2);
+
+ @media (max-width: $on-desktop) {
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+ }
+ }
+ }
+
+ .site-name {
+ color: var(--accent-color);
+ font-size: 2.4rem;
+ margin: 0;
+ }
+
+ .site-description {
+ color: var(--body-text-color);
+ font-weight: lighter;
+ margin: 10px 0;
+ font-size: 2rem;
+
+ @media (max-width: $on-desktop) {
+ font-size: 1.8rem;
+ }
+ }
+}
+
+.sidebar {
+ .widget {
+ &:not(:last-of-type) {
+ margin-bottom: 40px;
+ &:after {
+ content: "";
+ width: 100px;
+ height: 2px;
+ background-color: var(--body-text-color);
+ display: block;
+ margin-top: 40px;
+ }
+ }
+ }
+}
diff --git a/assets/scss/partials/widgets.scss b/assets/scss/partials/widgets.scss
new file mode 100644
index 0000000..105023e
--- /dev/null
+++ b/assets/scss/partials/widgets.scss
@@ -0,0 +1,79 @@
+.widget {
+ .widget-title {
+ text-transform: uppercase;
+ color: var(--body-text-color);
+ font-weight: normal;
+ margin: 0;
+ margin-bottom: 10px;
+ font-size: 1.6rem;
+ }
+
+ .widget-icon {
+ svg {
+ width: 32px;
+ height: 32px;
+ stroke-width: 1.33px;
+ color: var(--body-text-color);
+ }
+ }
+}
+
+/* Tag cloud widget */
+.tagCloud {
+ .tagCloud-tags {
+ display: flex;
+ flex-wrap: wrap;
+
+ a {
+ background: var(--card-background);
+ @include box_shadow(1);
+ border-radius: var(--tag-border-radius);
+ padding: 8px 20px;
+
+ color: var(--card-text-color-main);
+ margin-bottom: 10px;
+ margin-right: 5px;
+ font-size: 1.4rem;
+ }
+ }
+}
+
+/* Tweet widget */
+.tweet-container {
+ .tweet--item {
+ .tweet--text {
+ font-size: 1.6rem;
+ color: var(--accent-color);
+ line-height: 1.75;
+ }
+
+ .tweet--time {
+ text-transform: uppercase;
+ a {
+ color: var(--body-text-color);
+ }
+ }
+ }
+}
+
+.widget.archive {
+ .archive-year {
+ margin-bottom: 10px;
+ a {
+ background-color: var(--card-background);
+ padding: 15px 25px;
+ border-radius: var(--card-border-radius);
+ @include box_shadow(1);
+ display: flex;
+
+ span.year {
+ flex: 1;
+ color: var(--card-text-color-main);
+ }
+
+ span.count {
+ color: var(--card-text-color-tertiary);
+ }
+ }
+ }
+}
diff --git a/assets/scss/style.scss b/assets/scss/style.scss
new file mode 100644
index 0000000..bacbb8f
--- /dev/null
+++ b/assets/scss/style.scss
@@ -0,0 +1,131 @@
+@import "variables.scss";
+@import "helper/shadow.scss";
+
+@import "external/normalize.scss";
+
+@import "partials/menu.scss";
+@import "partials/article.scss";
+@import "partials/widgets.scss";
+@import "partials/footer.scss";
+@import "partials/pagination.scss";
+@import "partials/sidebar.scss";
+@import "partials/base.scss";
+@import "partials/layout/archive.scss";
+@import "partials/layout/article.scss";
+@import "partials/layout/taxonomy.scss";
+@import "partials/layout/404.scss";
+
+a {
+ text-decoration: none;
+ color: var(--accent-color);
+ transition: color 0.3s ease;
+ &:hover {
+ color: var(--accent-color-darker);
+ }
+}
+
+.container {
+ margin-left: auto;
+ margin-right: auto;
+
+ &.extended {
+ @media (min-width: $on-phone) {
+ max-width: 720px;
+ }
+
+ @media (min-width: $on-tablet) {
+ max-width: 972px;
+ }
+
+ @media (min-width: $on-desktop) {
+ max-width: 1200px;
+ }
+
+ @media (min-width: $on-desktop-large) {
+ max-width: 1536px;
+ }
+ }
+}
+
+#content {
+ @media (min-width: $on-phone) {
+ padding-top: 30px;
+ }
+
+ @media (min-width: $on-tablet) {
+ padding-top: 50px;
+ }
+}
+
+main.main {
+ min-width: 0;
+ padding: 0 15px;
+ max-width: 100%;
+ @media (min-width: $on-phone + 1) {
+ padding: 0 15px;
+ }
+ flex-grow: 1;
+}
+
+.main-grid {
+ @media (max-width: $on-phone) {
+ flex-direction: column;
+ }
+}
+
+.flex {
+ display: flex;
+ flex-direction: row;
+
+ &.column {
+ flex-direction: column;
+ }
+
+ &.on-phone--column {
+ @media (max-width: $on-phone) {
+ flex-direction: column;
+ }
+ }
+
+ &.align-items--flex-start {
+ align-items: flex-start;
+ }
+
+ .grow {
+ flex-grow: 1;
+ }
+
+ .do-not-shrink {
+ flex-shrink: 0;
+ }
+
+ .do-not-overflow {
+ min-width: 0;
+ flex-shrink: 1;
+ max-width: 100%;
+ }
+
+ .full-width {
+ width: 100%;
+ }
+}
+
+.alert {
+ position: fixed;
+ right: 20px;
+ bottom: 20px;
+ z-index: 5;
+ background: var(--card-background);
+ max-width: 400px;
+ padding: 15px 20px;
+ border-radius: var(--card-border-radius);
+ line-height: 1.75;
+ @include box_shadow(2);
+ color: var(--card-text-color-secondary);
+
+ @media (max-width: $on-phone) {
+ max-width: 100vw;
+ width: calc(100% - 30px);
+ left: 15px;
+ }
+}
diff --git a/assets/scss/variables.scss b/assets/scss/variables.scss
new file mode 100644
index 0000000..6812bf9
--- /dev/null
+++ b/assets/scss/variables.scss
@@ -0,0 +1,92 @@
+$fallback-font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial,
+ "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN",
+ "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp",
+ sans-serif;
+$base-font-family: "Lato", $fallback-font-family;
+$container-width: 1200px;
+$container-padding: 15px;
+
+$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
+$defaultTagColors: #fff, #fff, #fff, #fff, #fff;
+
+$on-phone: 740px;
+$on-tablet: 1024px;
+$on-desktop: 1519px;
+$on-desktop-large: 1920px;
+
+/*
+* CSS Variables
+*/
+$body-background: #f5f5fa;
+$accent-color: #34495e;
+$accent-color-darker: #2c3e50;
+$accent-color-text: #fff;
+
+$card-background: #fff;
+$card-background-selected: #eaeaea;
+
+$card-text-color-main: #000;
+$card-text-color-secondary: #747474;
+$card-text-color-tertiary: #bababa;
+$card-separator-color: rgba(218, 218, 218, 0.5);
+
+$card-border-radius: 10px;
+
+$body-text-color: #bababa;
+
+$tag-border-radius: 4px;
+
+:root {
+ --body-background: #{$body-background};
+ --accent-color: #{$accent-color};
+ --accent-color-darker: #{$accent-color-darker};
+ --accent-color-text: #{$accent-color-text};
+
+ --card-background: #{$card-background};
+ --card-background-selected: #{$card-background-selected};
+
+ --card-text-color-main: #{$card-text-color-main};
+ --card-text-color-secondary: #{$card-text-color-secondary};
+ --card-text-color-tertiary: #{$card-text-color-tertiary};
+ --card-separator-color: #{$card-separator-color};
+
+ --card-border-radius: #{$card-border-radius};
+
+ --body-text-color: #{$body-text-color};
+
+ --tag-border-radius: #{$tag-border-radius};
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ $body-background: #303030;
+ $accent-color: #ecf0f1;
+ $accent-color-darker: #bdc3c7;
+
+ $accent-color-text: #000;
+
+ $card-background: #424242;
+ $card-background-selected: rgba(255, 255, 255, 0.16);
+
+ $card-text-color-main: rgba(255, 255, 255, 0.9);
+ $card-text-color-secondary: rgba(255, 255, 255, 0.7);
+ $card-text-color-tertiary: rgba(255, 255, 255, 0.5);
+ $card-separator-color: rgba(255, 255, 255, 0.12);
+
+ $body-text-color: rgba(255, 255, 255, 0.7);
+
+ --body-background: #{$body-background};
+ --accent-color: #{$accent-color};
+ --accent-color-darker: #{$accent-color-darker};
+ --accent-color-text: #{$accent-color-text};
+
+ --card-background: #{$card-background};
+ --card-background-selected: #{$card-background-selected};
+ --card-text-color-main: #{$card-text-color-main};
+ --card-text-color-secondary: #{$card-text-color-secondary};
+ --card-text-color-tertiary: #{$card-text-color-tertiary};
+ --card-separator-color: #{$card-separator-color};
+
+ --body-text-color: #{$body-text-color};
+ }
+}
diff --git a/assets/ts/color.ts b/assets/ts/color.ts
new file mode 100644
index 0000000..938bc38
--- /dev/null
+++ b/assets/ts/color.ts
@@ -0,0 +1,49 @@
+interface colorScheme {
+ DarkMuted: {
+ hex: string,
+ rgb: Number[],
+ bodyTextColor: string
+ },
+ Vibrant: {
+ hex: string,
+ rgb: Number[],
+ bodyTextColor: string
+ }
+}
+
+let colorsCache: { [key: string]: colorScheme } = {};
+
+if (localStorage.hasOwnProperty('colorsCache')) {
+ try {
+ colorsCache = JSON.parse(localStorage.getItem('colorsCache'));
+ }
+ catch (e) {
+ colorsCache = {};
+ }
+}
+
+async function getColor(imageURL: string) {
+ if (!colorsCache.hasOwnProperty(imageURL)) {
+ const palette = await Vibrant.from(imageURL).getPalette();
+
+ colorsCache[imageURL] = {
+ Vibrant: {
+ hex: palette.Vibrant.hex,
+ rgb: palette.Vibrant.rgb,
+ bodyTextColor: palette.Vibrant.bodyTextColor
+ },
+ DarkMuted: {
+ hex: palette.DarkMuted.hex,
+ rgb: palette.DarkMuted.rgb,
+ bodyTextColor: palette.DarkMuted.bodyTextColor
+ }
+ }
+
+ localStorage.setItem('colorsCache', JSON.stringify(colorsCache));
+ }
+ return colorsCache[imageURL];
+}
+
+export {
+ getColor
+} \ No newline at end of file
diff --git a/assets/ts/gallery.ts b/assets/ts/gallery.ts
new file mode 100644
index 0000000..b9871ff
--- /dev/null
+++ b/assets/ts/gallery.ts
@@ -0,0 +1,287 @@
+import { loadScript, loadStyle } from './utils';
+
+/**
+ * Init PhotoSwipe
+ * From: https://photoswipe.com/documentation/getting-started.html
+ * @param gallerySelector
+ */
+var initPhotoSwipeFromDOM = function (gallerySelector) {
+
+ // parse slide data (url, title, size ...) from DOM elements
+ // (children of gallerySelector)
+ var parseThumbnailElements = function (el) {
+ var thumbElements = el.childNodes,
+ numNodes = thumbElements.length,
+ items = [],
+ figureEl,
+ linkEl,
+ size,
+ item;
+
+ for (var i = 0; i < numNodes; i++) {
+
+ figureEl = thumbElements[i]; // <figure> element
+
+ // include only element nodes
+ if (figureEl.nodeType !== 1) {
+ continue;
+ }
+
+ linkEl = figureEl.children[0]; // <a> element
+
+ size = linkEl.getAttribute('data-size').split('x');
+
+ // create slide object
+ item = {
+ src: linkEl.getAttribute('href'),
+ w: parseInt(size[0], 10),
+ h: parseInt(size[1], 10)
+ };
+
+
+
+ if (figureEl.children.length > 1) {
+ // <figcaption> content
+ item.title = figureEl.children[1].innerHTML;
+ }
+
+ if (linkEl.children.length > 0) {
+ // <img> thumbnail element, retrieving thumbnail url
+ item.msrc = linkEl.children[0].getAttribute('src');
+ }
+
+ item.el = figureEl; // save link to element for getThumbBoundsFn
+ items.push(item);
+ }
+
+ return items;
+ };
+
+ // find nearest parent element
+ var closest = function closest(el, fn) {
+ return el && (fn(el) ? el : closest(el.parentNode, fn));
+ };
+
+ // triggers when user clicks on thumbnail
+ var onThumbnailsClick = function (e) {
+ e = e || window.event;
+ e.preventDefault ? e.preventDefault() : e.returnValue = false;
+
+ var eTarget = e.target || e.srcElement;
+
+ // find root element of slide
+ var clickedListItem = closest(eTarget, function (el) {
+ return (el.tagName && el.tagName.toUpperCase() === 'FIGURE');
+ });
+
+ if (!clickedListItem) {
+ return;
+ }
+
+ // find index of clicked item by looping through all child nodes
+ // alternatively, you may define index via data- attribute
+ var clickedGallery = clickedListItem.parentNode,
+ childNodes = clickedListItem.parentNode.childNodes,
+ numChildNodes = childNodes.length,
+ nodeIndex = 0,
+ index;
+
+ for (var i = 0; i < numChildNodes; i++) {
+ if (childNodes[i].nodeType !== 1) {
+ continue;
+ }
+
+ if (childNodes[i] === clickedListItem) {
+ index = nodeIndex;
+ break;
+ }
+ nodeIndex++;
+ }
+
+ if (index >= 0) {
+ // open PhotoSwipe if valid index found
+ openPhotoSwipe(index, clickedGallery);
+ }
+ return false;
+ };
+
+ // parse picture index and gallery index from URL (#&pid=1&gid=2)
+ var photoswipeParseHash = function () {
+ var hash = window.location.hash.substring(1),
+ params = {};
+
+ if (hash.length < 5) {
+ return params;
+ }
+
+ var vars = hash.split('&');
+ for (var i = 0; i < vars.length; i++) {
+ if (!vars[i]) {
+ continue;
+ }
+ var pair = vars[i].split('=');
+ if (pair.length < 2) {
+ continue;
+ }
+ params[pair[0]] = pair[1];
+ }
+
+ if (params.gid) {
+ params.gid = parseInt(params.gid, 10);
+ }
+
+ return params;
+ };
+
+ var openPhotoSwipe = function (index, galleryElement, disableAnimation, fromURL) {
+ var pswpElement = document.querySelectorAll('.pswp')[0],
+ gallery,
+ options,
+ items;
+
+ items = parseThumbnailElements(galleryElement);
+
+ // define options (if needed)
+ options = {
+
+ // define gallery index (for URL)
+ galleryUID: galleryElement.getAttribute('data-pswp-uid'),
+
+ getThumbBoundsFn: function (index) {
+ // See Options -> getThumbBoundsFn section of documentation for more info
+ var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail
+ pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
+ rect = thumbnail.getBoundingClientRect();
+
+ return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
+ }
+
+ };
+
+ // PhotoSwipe opened from URL
+ if (fromURL) {
+ if (options.galleryPIDs) {
+ // parse real index when custom PIDs are used
+ // http://photoswipe.com/documentation/faq.html#custom-pid-in-url
+ for (var j = 0; j < items.length; j++) {
+ if (items[j].pid == index) {
+ options.index = j;
+ break;
+ }
+ }
+ } else {
+ // in URL indexes start from 1
+ options.index = parseInt(index, 10) - 1;
+ }
+ } else {
+ options.index = parseInt(index, 10);
+ }
+
+ // exit if index not found
+ if (isNaN(options.index)) {
+ return;
+ }
+
+ if (disableAnimation) {
+ options.showAnimationDuration = 0;
+ }
+
+ // Pass data to PhotoSwipe and initialize it
+ gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
+ gallery.init();
+ };
+
+ // loop through all gallery elements and bind events
+ var galleryElements = document.querySelectorAll(gallerySelector);
+
+ for (var i = 0, l = galleryElements.length; i < l; i++) {
+ galleryElements[i].setAttribute('data-pswp-uid', i + 1);
+ galleryElements[i].onclick = onThumbnailsClick;
+ }
+
+ // Parse URL and open gallery if it contains #&pid=3&gid=1
+ var hashData = photoswipeParseHash();
+ if (hashData.pid && hashData.gid) {
+ openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
+ }
+};
+
+/**
+ * Wrap adjacent figure tags with div.gallery, and append style
+ * Reference: https://github.com/xieranmaya/blog/issues/6
+ * @param gallery
+ */
+function wrap(gallery: HTMLElement[]) {
+ let galleryContainer = document.createElement('div');
+ galleryContainer.className = 'gallery';
+
+ let parentNode = gallery[0].parentNode,
+ first = gallery[0];
+
+ parentNode.insertBefore(galleryContainer, first)
+
+ for (let j = 0; j < gallery.length; ++j) {
+ const width = gallery[j].querySelector('img').width,
+ height = gallery[j].querySelector('img').height;
+
+ gallery[j].style.flexGrow = `${width * 100 / height}`;
+ gallery[j].style.flexBasis = `${width * 240 / height}px`;
+
+ galleryContainer.appendChild(gallery[j]);
+ }
+}
+
+/**
+ * Load PhotoSwipe library dynamically
+ */
+function loadPhotoSwipe() {
+ const tasks = [
+ loadScript("https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"),
+ loadScript("https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"),
+ loadStyle("https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.css"),
+ loadStyle("https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/default-skin/default-skin.min.css")
+ ];
+
+ return Promise.all(tasks);
+}
+
+/**
+ * Create gallery
+ * @param selector
+ */
+function createGallery(selector: string) {
+ const figures = document.querySelector(selector).querySelectorAll('figure');
+
+ if (figures.length) {
+ let currentGallery = [figures[0]];
+ for (let i = 1; i < figures.length; ++i) {
+ if (figures[i].previousElementSibling === currentGallery[currentGallery.length - 1]) {
+ /* Adjacent */
+ currentGallery.push(figures[i]);
+ }
+ else {
+ /* End gallery */
+ wrap(currentGallery);
+ currentGallery = [figures[i]];
+ }
+ }
+
+ if (currentGallery.length > 0) {
+ wrap(currentGallery);
+ }
+
+ /**
+ * Load PhotoSwipe library, and then initialize
+ */
+ loadPhotoSwipe().then(() => {
+ const pswp = document.querySelector('.pswp') as HTMLDivElement;
+ pswp.style.removeProperty('display');
+
+ initPhotoSwipeFromDOM(`${selector} .gallery`);
+ })
+ }
+}
+
+export {
+ createGallery
+} \ No newline at end of file
diff --git a/assets/ts/main.ts b/assets/ts/main.ts
new file mode 100644
index 0000000..07c0413
--- /dev/null
+++ b/assets/ts/main.ts
@@ -0,0 +1,96 @@
+import { createGallery } from "./gallery"
+import { getColor } from './color';
+import menu from './menu';
+
+let Stack = {
+ init: () => {
+ /**
+ * Bind menu event
+ */
+ menu();
+
+ /**
+ * Add timeago
+ */
+ const timeagoElements = document.querySelectorAll('article time[data-timeago=true]');
+ if (timeagoElements.length) {
+ timeago.render(timeagoElements);
+ }
+
+ if (document.querySelector('.article-content')) {
+ createGallery('.article-content');
+ }
+
+ /**
+ * Add color to tags
+ */
+ document.querySelectorAll('.color-tag').forEach(async (tag: HTMLLinkElement) => {
+ const imageURL = tag.getAttribute('data-image');
+
+ const colors = await getColor(imageURL);
+
+ tag.style.color = colors.Vibrant.bodyTextColor;
+ tag.style.background = colors.Vibrant.hex;
+ })
+
+ /**
+ * Add linear gradient background to tile style article
+ */
+ const articleTile = document.querySelector('.article-list--tile');
+ if (articleTile) {
+ let observer = new IntersectionObserver(async (entries, observer) => {
+ entries.forEach(entry => {
+ if (!entry.isIntersecting) return;
+ observer.unobserve(entry.target);
+
+ const articles = entry.target.querySelectorAll('article.has-image');
+ articles.forEach(async articles => {
+ const image = articles.querySelector('img'),
+ imageURL = image.src,
+ articleDetails: HTMLDivElement = articles.querySelector('.article-details');
+
+ const colors = await getColor(imageURL);
+
+ articleDetails.style.background = `
+ linear-gradient(0deg,
+ rgba(${colors.DarkMuted.rgb[0]}, ${colors.DarkMuted.rgb[1]}, ${colors.DarkMuted.rgb[2]}, 0.5) 0%,
+ rgba(${colors.Vibrant.rgb[0]}, ${colors.Vibrant.rgb[1]}, ${colors.Vibrant.rgb[2]}, 0.75) 100%)`;
+ })
+ })
+ });
+
+ observer.observe(articleTile)
+ }
+ },
+ alert: (content, time = 5000, animationSpeed = 500) => {
+ const alert = document.createElement('div');
+ alert.innerHTML = content;
+ alert.className = 'alert';
+ alert.style.visibility = 'hidden';
+ document.body.appendChild(alert);
+
+ alert.style.transform = `translateY(${alert.clientHeight + 50}px)`;
+ alert.style.transition = `transform ${animationSpeed / 1000}s ease`;
+
+ setTimeout(() => {
+ alert.style.removeProperty('visibility');
+ alert.style.transform = `translateY(0)`;
+ }, animationSpeed);
+
+ setTimeout(() => {
+ alert.style.transform = `translateY(${alert.clientHeight + 50}px)`;
+ }, animationSpeed + time);
+
+ setTimeout(() => {
+ alert.remove();
+ }, 2 * animationSpeed + time);
+ }
+}
+
+window.addEventListener('load', () => {
+ setTimeout(function () {
+ Stack.init();
+ }, 0);
+})
+
+window.Stack = Stack; \ No newline at end of file
diff --git a/assets/ts/menu.ts b/assets/ts/menu.ts
new file mode 100644
index 0000000..34615ba
--- /dev/null
+++ b/assets/ts/menu.ts
@@ -0,0 +1,83 @@
+/**
+ * Slide up/down
+ * Code from https://dev.to/bmsvieira/vanilla-js-slidedown-up-4dkn
+ * @param target
+ * @param duration
+ */
+let slideUp = (target: HTMLElement, duration = 500) => {
+ target.classList.add('transiting');
+ target.style.transitionProperty = 'height, margin, padding';
+ target.style.transitionDuration = duration + 'ms';
+ ///target.style.boxSizing = 'border-box';
+ target.style.height = target.offsetHeight + 'px';
+ target.offsetHeight;
+ target.style.overflow = 'hidden';
+ target.style.height = "0";
+ target.style.paddingTop = "0";
+ target.style.paddingBottom = "0";
+ target.style.marginTop = "0";
+ target.style.marginBottom = "0";
+ window.setTimeout(() => {
+ target.classList.remove('show')
+ target.style.removeProperty('height');
+ target.style.removeProperty('padding-top');
+ target.style.removeProperty('padding-bottom');
+ target.style.removeProperty('margin-top');
+ target.style.removeProperty('margin-bottom');
+ target.style.removeProperty('overflow');
+ target.style.removeProperty('transition-duration');
+ target.style.removeProperty('transition-property');
+ target.classList.remove('transiting');
+ }, duration);
+}
+
+let slideDown = (target: HTMLElement, duration = 500) => {
+ target.classList.add('transiting');
+ target.style.removeProperty('display');
+
+ target.classList.add('show');
+
+ let height = target.offsetHeight;
+ target.style.overflow = 'hidden';
+ target.style.height = "0";
+ target.style.paddingTop = "0";
+ target.style.paddingBottom = "0";
+ target.style.marginTop = "0";
+ target.style.marginBottom = "0";
+ target.offsetHeight;
+ ///target.style.boxSizing = 'border-box';
+ target.style.transitionProperty = "height, margin, padding";
+ target.style.transitionDuration = duration + 'ms';
+ target.style.height = height + 'px';
+ target.style.removeProperty('padding-top');
+ target.style.removeProperty('padding-bottom');
+ target.style.removeProperty('margin-top');
+ target.style.removeProperty('margin-bottom');
+ window.setTimeout(() => {
+ target.style.removeProperty('height');
+ target.style.removeProperty('overflow');
+ target.style.removeProperty('transition-duration');
+ target.style.removeProperty('transition-property');
+ target.classList.remove('transiting');
+ }, duration);
+}
+
+let slideToggle = (target, duration = 500) => {
+ if (window.getComputedStyle(target).display === 'none') {
+ return slideDown(target, duration);
+ } else {
+ return slideUp(target, duration);
+ }
+}
+
+export default function () {
+ const toggleMenu = document.getElementById('toggle-menu');
+ if (toggleMenu) {
+ toggleMenu.addEventListener('click', () => {
+ if (document.getElementById('main-menu').classList.contains('transiting')) return;
+ document.body.classList.toggle('show-menu');
+ slideToggle(document.getElementById('main-menu'), 300);
+ toggleMenu.classList.toggle('is-active');
+ });
+ }
+} \ No newline at end of file
diff --git a/assets/ts/utils.ts b/assets/ts/utils.ts
new file mode 100644
index 0000000..f725ca3
--- /dev/null
+++ b/assets/ts/utils.ts
@@ -0,0 +1,43 @@
+/**
+ * Load script asynchronous
+ * @return {Promise}
+ * @param url
+ */
+const loadScript = function (url) {
+ return new Promise(resolve => {
+ var scriptTag = document.createElement('script');
+ scriptTag.src = url;
+
+ scriptTag.onload = () => {
+ resolve();
+ };
+
+ document.head.appendChild(scriptTag);
+ })
+};
+
+/**
+ * Load style asynchronous
+ * @return {Promise}
+ * @param url
+ */
+const loadStyle = function (url) {
+ return new Promise(resolve => {
+ var link = document.createElement('link');
+ link.href = url;
+
+ link.type = "text/css";
+ link.rel = "stylesheet";
+
+ link.onload = () => {
+ resolve();
+ };
+
+ document.head.appendChild(link);
+ });
+};
+
+export {
+ loadScript,
+ loadStyle
+} \ No newline at end of file
diff --git a/layouts/404.html b/layouts/404.html
new file mode 100644
index 0000000..2d4565a
--- /dev/null
+++ b/layouts/404.html
@@ -0,0 +1,13 @@
+{{ define "body_class" }}2-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column align-items--flex-start">
+ {{ partial "sidebar/left.html" . }}
+
+ <main class="main full-width">
+ <div class="not-found-card">
+ <h1 class="article-title">Not Found</h1>
+ <h2 class="article-subtitle">This page does not exist.</h2>
+ </div>
+ </main>
+</div>
+{{ end }} \ No newline at end of file
diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html
new file mode 100644
index 0000000..1502c0e
--- /dev/null
+++ b/layouts/_default/_markup/render-image.html
@@ -0,0 +1,18 @@
+{{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}}
+{{- $small := $image.Resize "480x" -}}
+{{- $big := $image.Resize "1024x" -}}
+{{- $alt := .PlainText | safeHTML -}}
+{{- $caption := "" -}}
+{{- with $alt -}}
+{{- $caption = . | safeHTML -}}
+{{- end -}}
+<figure>
+ <a href="{{ $image.RelPermalink }}" data-size="{{ $image.Width }}x{{ $image.Height }}">
+ <img srcset="{{ $small.RelPermalink }} 480w, {{ $big.RelPermalink }} 1024w"
+ src="{{ $image.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
+ alt="{{ if $alt }}{{ $alt }}{{ else if $caption }}{{ $caption | markdownify | plainify }}{{ else }}&nbsp;{{ end }}">
+ </a>
+ {{ with $caption }}
+ <figcaption>{{ . | markdownify }}</figcaption>
+ {{ end }}
+</figure> \ No newline at end of file
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
new file mode 100644
index 0000000..bb2e8f8
--- /dev/null
+++ b/layouts/_default/baseof.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="{{ .Site.LanguageCode }}">
+ {{- partial "head.html" . -}}
+ <body>
+ <div id="content">
+ {{- block "main" . }}{{- end }}
+ </div>
+ {{ partial "footer/script.html" . }}
+ {{ partial "footer/style.html" . }}
+ </body>
+</html>
diff --git a/layouts/_default/single.html b/layouts/_default/single.html
new file mode 100644
index 0000000..3fe2859
--- /dev/null
+++ b/layouts/_default/single.html
@@ -0,0 +1,30 @@
+{{ define "body_class" }}2-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column align-items--flex-start article-page">
+ {{ partial "sidebar/left.html" . }}
+
+ <div class="flex column do-not-overflow">
+ <main class="main">
+ <div id="article-toolbar">
+ <a href="{{ .Site.BaseURL }}" class="back-home">
+ {{ (resources.Get "icons/back.svg").Content | safeHTML }}
+ <span>Back</span>
+ </a>
+ </div>
+
+ {{ partial "article/article.html" . }}
+
+ {{ partial "article/components/related-contents" . }}
+
+ {{ if or (not (isset .Params "comments")) (eq .Params.comments "true")}}
+ {{ partial "article/components/comment" . }}
+ {{ end }}
+
+ {{ partialCached "footer" . }}
+ </main>
+ </div>
+</div>
+
+{{- partial "article/components/photoswipe.html" . -}}
+
+{{ end }} \ No newline at end of file
diff --git a/layouts/_default/term.html b/layouts/_default/term.html
new file mode 100644
index 0000000..cae47d1
--- /dev/null
+++ b/layouts/_default/term.html
@@ -0,0 +1,41 @@
+{{ define "body_class" }}2-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column">
+ {{ partial "sidebar/left.html" . }}
+
+ <main class="main">
+
+ <h3 class="taxonomy-type">{{ .Type | singularize | humanize }}</h3>
+ <div class="taxonomy-card">
+ <div class="taxonomy-details">
+ <h3 class="taxonomy-count">{{ len .Pages }} post{{ if gt (len .Pages) 1 }}s{{ end }}</h3>
+ <h1 class="taxonomy-term">{{ .Title }}</h1>
+ {{ with .Params.description }}
+ <h2 class="taxonomy-description">{{ . }}</h2>
+ {{ end }}
+ </div>
+
+ {{ if .Params.image }}
+ {{- $image := partial "helper/image" . -}}
+ {{- $thumbnail := $image.Fill "120x120" -}}
+ <div class="taxonomy-image">
+ <img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}"
+ height="{{ $thumbnail.Height }}" loading="lazy">
+ </div>
+ {{ end }}
+ </div>
+
+ <section class="article-list--compact">
+ {{ $v2 := where .Pages "Params.hidden" "!=" true }}
+ {{ $pag := .Paginate (.Pages) }}
+ {{ range $pag.Pages }}
+ {{ partial "article-list/compact" . }}
+ {{ end }}
+ </section>
+
+ {{- partial "pagination.html" . -}}
+
+ {{ partial "footer" . }}
+ </main>
+</div>
+{{ end }} \ No newline at end of file
diff --git a/layouts/index.html b/layouts/index.html
new file mode 100644
index 0000000..03af128
--- /dev/null
+++ b/layouts/index.html
@@ -0,0 +1,25 @@
+{{ define "body_class" }}3-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column align-items--flex-start">
+ {{ partialCached "sidebar/left.html" . }}
+
+ <main class="main full-width">
+ {{ $postSection := $.Site.Params.postSection }}
+ {{ $v1 := where .Site.RegularPages "Section" $postSection }}
+ {{ $v2 := where .Site.RegularPages "Params.hidden" "!=" true }}
+ {{ $.Scratch.Set "filtered" ($v1 | intersect $v2) }}
+ {{ $pag := .Paginate ($.Scratch.Get "filtered") }}
+
+ <section class="article-list">
+ {{ range $index, $element := $pag.Pages }}
+ {{ partial "article-list/default" . }}
+ {{ end }}
+ {{- partial "pagination.html" . -}}
+ </section>
+
+ {{ partialCached "footer" . }}
+ </main>
+
+ {{ partialCached "sidebar/right.html" . }}
+</div>
+{{ end }} \ No newline at end of file
diff --git a/layouts/page/archive.html b/layouts/page/archive.html
new file mode 100644
index 0000000..c63544e
--- /dev/null
+++ b/layouts/page/archive.html
@@ -0,0 +1,39 @@
+{{ define "body_class" }}2-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column align-items--flex-start">
+
+ {{ partial "sidebar/left.html" . }}
+
+ <main class="main template-archive">
+ <div class="widget">
+ <h1 class="widget-title">Categories</h1>
+ <div class="category-list">
+ <div class="article-list--tile">
+ {{ range ($.Site.GetPage "taxonomyTerm" "categories").Pages }}
+ {{ partial "article-list/tile" (dict "context" . "size" "250x150") }}
+ {{ end }}
+ </div>
+ </div>
+ </div>
+
+ {{ $postSection := $.Site.Params.postSection }}
+ {{ $v1 := where .Site.RegularPages "Section" $postSection }}
+ {{ $v2 := where .Site.RegularPages "Params.hidden" "!=" true }}
+ {{ $filtered := $v1 | intersect $v2 }}
+ {{ range $filtered.GroupByDate "2006" }}
+ {{ $id := lower (replace .Key " " "-") }}
+ <div class="archive-group" id="{{ $id }}">
+ <h3 class="archive-date"><a href="{{ $.Permalink }}#{{ $id }}">{{ .Key }}</a></h3>
+ <div class="article-list--compact">
+ {{ range .Pages }}
+ {{ partial "article-list/compact" . }}
+ {{ end }}
+ </div>
+ </div>
+ {{ end }}
+
+ {{ partial "footer.html" . }}
+ </main>
+</div>
+
+{{ end }} \ No newline at end of file
diff --git a/layouts/page/single.html b/layouts/page/single.html
new file mode 100644
index 0000000..d1791f0
--- /dev/null
+++ b/layouts/page/single.html
@@ -0,0 +1,18 @@
+{{ define "body_class" }}2-column{{ end }}
+{{ define "main" }}
+<div class="container extended flex on-phone--column align-items--flex-start article-and-sidebar">
+
+ {{ partial "sidebar/left.html" . }}
+
+ <main class="main article-page do-not-overflow full-width">
+ {{ partial "article/article.html" . }}
+
+ {{ if or (not (isset .Params "comments")) (eq .Params.comments "true")}}
+ {{ partial "article/components/comment" . }}
+ {{ end }}
+ </main>
+</div>
+
+{{ partial "article/components/photoswipe" . }}
+
+{{ end }} \ No newline at end of file
diff --git a/layouts/partials/article-list/compact.html b/layouts/partials/article-list/compact.html
new file mode 100644
index 0000000..02fc0b3
--- /dev/null
+++ b/layouts/partials/article-list/compact.html
@@ -0,0 +1,24 @@
+<article>
+ <div class="article-details">
+ <h2 class="article-title">
+ <a href="{{ .Permalink }}">
+ {{- .Title -}}
+ </a>
+ </h2>
+ <footer class="article-time">
+ <time datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
+ {{- .Date.Format ( or .Site.Params.dateFormat "Jan 02, 2006" ) -}}
+ </time>
+ </footer>
+ </div>
+
+ {{ if .Params.image }}
+ {{- $image := partial "helper/image" . -}}
+ {{- $thumbnail := $image.Fill "120x120" -}}
+
+ <div class="article-image">
+ <img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}"
+ height="{{ $thumbnail.Height }}" loading="lazy">
+ </div>
+ {{ end }}
+</article> \ No newline at end of file
diff --git a/layouts/partials/article-list/default.html b/layouts/partials/article-list/default.html
new file mode 100644
index 0000000..ed9fc43
--- /dev/null
+++ b/layouts/partials/article-list/default.html
@@ -0,0 +1,51 @@
+<article class="{{ if .Params.image }}has-image{{ end }}">
+ {{ if .Params.image }}
+ {{- $image := partial "helper/image" . -}}
+ {{- $thumbnailNotDesktop := $image.Resize "x500 smart" -}}
+
+ <div class="article-image">
+ <img src="{{ $thumbnailNotDesktop.RelPermalink }}"
+ width="{{ $thumbnailNotDesktop.Width }}" height="{{ $thumbnailNotDesktop.Height }}" loading="lazy"
+ alt="Featured image of post {{ .Title }}" />
+ </div>
+ {{ end }}
+
+ {{ $i := .Params.image }}
+ {{ $context := . }}
+
+ <div class="article-details">
+ {{ with $categories := .Params.categories }}
+ <header class="article-category">
+ {{ range first 1 $categories }}
+ {{ if $i }}
+ {{- $image := partial "helper/image" $context -}}
+ {{- $20x := $image.Fill "20x20 smart" -}}
+ <a href="/categories/{{ . | urlize }}" class="color-tag"
+ data-image="{{ $20x.RelPermalink }}">{{ . | humanize }}</a>
+ {{ else }}
+ <a href="/categories/{{ . | urlize }}">{{ . | humanize }}</a>
+ {{ end }}
+ {{ end }}
+ </header>
+ {{ end }}
+
+ <h2 class="article-title">
+ <a href="{{ .Permalink }}">
+ {{- .Title -}}
+ </a>
+ </h2>
+
+ {{ with .Params.description }}
+ <h3 class="article-subtitle">
+ {{ . }}
+ </h3>
+ {{ end }}
+
+ <footer class="article-time">
+ {{ (resources.Get "icons/clock.svg").Content | safeHTML }}
+ <time data-timeago="true" datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
+ {{- .Date.Format ( or .Site.Params.dateFormat "Jan 02, 2006" ) -}}
+ </time>
+ </footer>
+ </div>
+</article> \ No newline at end of file
diff --git a/layouts/partials/article-list/tile.html b/layouts/partials/article-list/tile.html
new file mode 100644
index 0000000..1602651
--- /dev/null
+++ b/layouts/partials/article-list/tile.html
@@ -0,0 +1,17 @@
+<article class="{{ if .context.Params.image }}has-image{{ end }}">
+ <a href="{{ .context.Permalink }}">
+ {{ if .context.Params.image }}
+ {{- $thumbnail := (partial "helper/image" (.context) ).Fill .size -}}
+ <div class="article-image">
+ <img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}" height="{{ $thumbnail.Height }}"
+ loading="lazy">
+ </div>
+ {{ end }}
+
+ <div class="article-details">
+ <h2 class="article-title">
+ {{- .context.Title -}}
+ </h2>
+ </div>
+ </a>
+</article> \ No newline at end of file
diff --git a/layouts/partials/article/article.html b/layouts/partials/article/article.html
new file mode 100644
index 0000000..6748188
--- /dev/null
+++ b/layouts/partials/article/article.html
@@ -0,0 +1,75 @@
+<article class="{{ if .Params.image }}has-image{{ end }} main-article">
+ <header class="article-header">
+ {{ if .Params.image }}
+ {{- $image := partial "helper/image" . -}}
+
+ {{- $tablet := $image.Resize "1024x" -}}
+ {{- $desktop := $image.Resize "2000x" -}}
+
+ {{- $20x := $image.Fill "20x20 smart" -}}
+ {{- .Scratch.Set "20x" $20x -}}
+
+ <div class="article-image">
+ <img srcset="{{ $tablet.RelPermalink }} 1024w, {{ $desktop.RelPermalink }} 2000w"
+ src="{{ $desktop.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}"
+ loading="lazy"
+ alt="Featured image of post {{ .Title }}" />
+ </div>
+ {{ end }}
+
+ <div class="article-details">
+ {{ with $category := .Params.categories }}
+ <header class="article-category">
+ {{ range first 1 $category }}
+ {{ if $.Params.image }}
+ <a href="/categories/{{ . | urlize }}" class="color-tag"
+ data-image="{{ ($.Scratch.Get "20x").RelPermalink }}">{{ . | humanize }}</a>
+ {{ else }}
+ <a href="/categories/{{ . | urlize }}">{{ . | humanize }}</a>
+ {{ end }}
+ {{ end }}
+ </header>
+ {{ end }}
+
+ <h2 class="article-title">
+ <a href="{{ .Permalink }}">
+ {{- .Title -}}
+ </a>
+ </h2>
+
+ {{ with .Params.description }}
+ <h3 class="article-subtitle">
+ {{ . }}
+ </h3>
+ {{ end }}
+
+ <footer class="article-time">
+ {{ (resources.Get "icons/clock.svg").Content | safeHTML }}
+ <time datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
+ {{- .Date.Format ( or .Site.Params.dateFormat "Jan 02, 2006" ) -}}
+ </time>
+ </footer>
+ </div>
+ </header>
+
+ <section class="article-content">
+ {{ .Content }}
+ </section>
+
+ <footer class="article-footer">
+ {{ with $tags := .Params.Tags }}
+ <section class="article-tags">
+ {{ range $tags }}
+ <a href="/tags/{{ . | urlize }}">{{ . | humanize }}</a>
+ {{ end }}
+ </section>
+ {{ end }}
+
+ {{ if .Site.Params.postLicense }}
+ <section class="article-copyright">
+ {{ (resources.Get "icons/copyright.svg").Content | safeHTML }}
+ <span>{{ .Site.Params.postLicense }}</span>
+ </section>
+ {{ end }}
+ </footer>
+</article> \ No newline at end of file
diff --git a/layouts/partials/article/components/comment.html b/layouts/partials/article/components/comment.html
new file mode 100644
index 0000000..393568b
--- /dev/null
+++ b/layouts/partials/article/components/comment.html
@@ -0,0 +1 @@
+{{ template "_internal/disqus.html" . }} \ No newline at end of file
diff --git a/layouts/partials/article/components/photoswipe.html b/layouts/partials/article/components/photoswipe.html
new file mode 100644
index 0000000..290872c
--- /dev/null
+++ b/layouts/partials/article/components/photoswipe.html
@@ -0,0 +1,66 @@
+<!-- Root element of PhotoSwipe. Must have class pswp. -->
+<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="display:none">
+
+ <!-- Background of PhotoSwipe.
+ It's a separate element as animating opacity is faster than rgba(). -->
+ <div class="pswp__bg"></div>
+
+ <!-- Slides wrapper with overflow:hidden. -->
+ <div class="pswp__scroll-wrap">
+
+ <!-- Container that holds slides.
+ PhotoSwipe keeps only 3 of them in the DOM to save memory.
+ Don't modify these 3 pswp__item elements, data is added later on. -->
+ <div class="pswp__container">
+ <div class="pswp__item"></div>
+ <div class="pswp__item"></div>
+ <div class="pswp__item"></div>
+ </div>
+
+ <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
+ <div class="pswp__ui pswp__ui--hidden">
+
+ <div class="pswp__top-bar">
+
+ <!-- Controls are self-explanatory. Order can be changed. -->
+
+ <div class="pswp__counter"></div>
+
+ <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
+
+ <button class="pswp__button pswp__button--share" title="Share"></button>
+
+ <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
+
+ <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
+
+ <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
+ <!-- element will get class pswp__preloader--active when preloader is running -->
+ <div class="pswp__preloader">
+ <div class="pswp__preloader__icn">
+ <div class="pswp__preloader__cut">
+ <div class="pswp__preloader__donut"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
+ <div class="pswp__share-tooltip"></div>
+ </div>
+
+ <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
+ </button>
+
+ <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
+ </button>
+
+ <div class="pswp__caption">
+ <div class="pswp__caption__center"></div>
+ </div>
+
+ </div>
+
+ </div>
+
+</div> \ No newline at end of file
diff --git a/layouts/partials/article/components/related-contents.html b/layouts/partials/article/components/related-contents.html
new file mode 100644
index 0000000..972c979
--- /dev/null
+++ b/layouts/partials/article/components/related-contents.html
@@ -0,0 +1,13 @@
+<aside class="widget related-contents--wrapper">
+ {{ $related := .Site.RegularPages.Related . | first 5 }}
+ {{ with $related }}
+ <h1 class="widget-title">Related contents</h1>
+ <div class="related-contents">
+ <div class="flex article-list--tile">
+ {{ range . }}
+ {{ partial "article-list/tile" (dict "context" . "size" "250x350") }}
+ {{ end }}
+ </div>
+ </div>
+ {{ end }}
+</aside> \ No newline at end of file
diff --git a/layouts/partials/data/description.html b/layouts/partials/data/description.html
new file mode 100644
index 0000000..5531ab5
--- /dev/null
+++ b/layouts/partials/data/description.html
@@ -0,0 +1,11 @@
+{{- with .Description -}}
+ {{- . -}}
+{{- else -}}
+ {{- if .IsPage -}}
+ {{- .Summary -}}
+ {{- else -}}
+ {{- with .Site.Params.subtitle -}}
+ {{- . -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}} \ No newline at end of file
diff --git a/layouts/partials/data/title.html b/layouts/partials/data/title.html
new file mode 100644
index 0000000..7249649
--- /dev/null
+++ b/layouts/partials/data/title.html
@@ -0,0 +1,16 @@
+{{- $title := .Title -}}
+{{- $siteTitle := .Site.Title -}}
+{{- $authorName := .Site.Author.name -}}
+
+{{- if .IsHome -}}
+ {{- $v1 := where .Site.RegularPages "Type" "post" -}}
+ {{- $v2 := where .Site.RegularPages "Params.hidden" "!=" true -}}
+ {{- $filtered := $v1 | intersect $v2 -}}
+ {{- $pag := .Paginate ($filtered) -}}
+ {{ if .Paginator.HasPrev }}{{ .Paginator }} - {{ end }}{{ $siteTitle }}
+{{- else if eq .Kind "term" -}}
+ {{- $pag := .Paginate (where .Data.Pages "Type" "post") -}}
+ {{ title .Data.Singular }}: {{ $title }}{{ if .Paginator.HasPrev }} - {{ .Paginator }}{{ end }} - {{ $siteTitle }}
+{{- else -}}
+ {{- $title -}}
+{{- end -}} \ No newline at end of file
diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html
new file mode 100644
index 0000000..a1be85f
--- /dev/null
+++ b/layouts/partials/footer.html
@@ -0,0 +1,7 @@
+<footer class="site-footer">
+ <section class="copyright">&copy; {{ now.Format "2006" }} {{ .Site.Title }}</section>
+ <section class="powerby">
+ Built with <a href="https://gohugo.io/" target="_blank">Hugo</a> <br />
+ Theme <b>Stack</b> designed by <a href="https://jimmycai.com" target="_blank">Jimmy</a>
+ </section>
+</footer> \ No newline at end of file
diff --git a/layouts/partials/footer/script.html b/layouts/partials/footer/script.html
new file mode 100644
index 0000000..50cef14
--- /dev/null
+++ b/layouts/partials/footer/script.html
@@ -0,0 +1,15 @@
+<script src="https://cdnjs.cloudflare.com/ajax/libs/timeago.js/4.0.2/timeago.min.js"
+ integrity="sha512-SVDh1zH5N9ChofSlNAK43lcNS7lWze6DTVx1JCXH1Tmno+0/1jMpdbR8YDgDUfcUrPp1xyE53G42GFrcM0CMVg=="
+ crossorigin="anonymous"></script>
+
+<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-JavaScript-Templates/3.18.0/js/tmpl.min.js"
+ integrity="sha512-62X328c9VQQkWxrLMccNyRISbwvqQDjvF/HFuvHBMGtZJbNvTG30k1M2O+PYLyWUrcHFKIPvr2OkgmUmcaiccw=="
+ crossorigin="anonymous"></script>-->
+
+<script src="https://cdn.jsdelivr.net/npm/node-vibrant@3.1.5/dist/vibrant.min.js"
+ integrity="sha256-5NovOZc4iwiAWTYIFiIM7DxKUXKWvpVEuMEPLzcm5/g=" crossorigin="anonymous"></script>
+
+{{ $opts := dict "minify" hugo.IsProduction }}
+{{ $script := resources.Get "ts/main.ts" | js.Build $opts }}
+
+<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script> \ No newline at end of file
diff --git a/layouts/partials/footer/style.html b/layouts/partials/footer/style.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/layouts/partials/footer/style.html
diff --git a/layouts/partials/head.html b/layouts/partials/head.html
new file mode 100644
index 0000000..6543065
--- /dev/null
+++ b/layouts/partials/head.html
@@ -0,0 +1,18 @@
+<head>
+ <meta charset='utf-8'>
+ <meta name='viewport' content='width=device-width, initial-scale=1'>
+ <meta name='description' content='{{ chomp (partial "data/description" . | plainify ) }}'>
+ <base href='{{ .Site.BaseURL }}'>
+
+ <title>{{ (trim (partial "data/title" .) "\n" ) | safeHTML }}</title>
+ <link rel='canonical' href='{{ .Permalink }}'>
+
+ {{ partial "head/style.html" . }}
+ {{ partial "head/script.html" . }}
+
+ {{ partial "head/opengraph.html" . }}
+
+ {{ range .AlternativeOutputFormats -}}
+ <link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
+ {{ end -}}
+</head> \ No newline at end of file
diff --git a/layouts/partials/head/opengraph.html b/layouts/partials/head/opengraph.html
new file mode 100644
index 0000000..4e9c368
--- /dev/null
+++ b/layouts/partials/head/opengraph.html
@@ -0,0 +1,51 @@
+<meta property='og:title' content='{{ partial "data/title" . }}'>
+<meta property='og:description' content='{{ chomp (partial "data/description" . | plainify ) }}'>
+<meta property='og:url' content='{{ .Permalink }}'>
+<meta property='og:site_name' content='{{ .Site.Title }}'>
+<meta property='og:type' content='
+ {{- if .IsPage -}}
+ article
+ {{- else -}}
+ website
+ {{- end -}}
+'>
+
+{{ with .Site.Params.twitter }}
+<meta name="twitter:site" content="{{ . }}">
+{{ end }}
+<meta name="twitter:title" content="{{ partial "data/title" . }}">
+<meta name="twitter:description" content="{{ chomp (partial "data/description" . | plainify ) }}">
+
+{{- with .Params.locale -}}
+ <meta property='og:locale' content='{{ . }}'>
+{{- end -}}
+
+{{- if .IsPage -}}
+ <meta property='article:section' content='{{ .Section | title }}' />
+ {{- range .Params.tags -}}
+ <meta property='article:tag' content='{{ . }}' />
+ {{- end -}}
+{{- end -}}
+
+{{- if .IsPage -}}
+ {{- if not .Date.IsZero -}}
+ <meta property='article:published_time' content='{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
+ {{- end -}}
+ {{- if not .Lastmod.IsZero -}}
+ <meta property='article:modified_time' content='{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
+ {{- end -}}
+{{- else -}}
+ {{- if not .Site.LastChange.IsZero -}}
+ <meta property='og:updated_time' content='{{ .Site.LastChange.Format " 2006-01-02T15:04:05-07:00 " | safeHTML }}'/>
+ {{- end -}}
+{{- end -}}
+
+<meta name="twitter:card" content="summary_large_image">
+
+{{- if .Params.image -}}
+ {{ $image := partial "helper/image" . }}
+ <meta property='og:image' content='{{ absURL $image.RelPermalink }}' />
+ <meta name="twitter:image" content='{{ absURL $image.RelPermalink }}' />
+{{- else -}}
+ <meta property='og:image' content='{{ absURL .Site.Params.logo }}' />
+{{- end -}} \ No newline at end of file
diff --git a/layouts/partials/head/script.html b/layouts/partials/head/script.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/layouts/partials/head/script.html
diff --git a/layouts/partials/head/style.html b/layouts/partials/head/style.html
new file mode 100644
index 0000000..3dfc2bd
--- /dev/null
+++ b/layouts/partials/head/style.html
@@ -0,0 +1,5 @@
+<link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap" rel="stylesheet">
+
+{{ $sass := resources.Get "scss/style.scss" }}
+{{ $style := $sass | resources.ToCSS | minify }}
+<link rel="stylesheet" href="{{ $style.Permalink }}"> \ No newline at end of file
diff --git a/layouts/partials/helper/image.html b/layouts/partials/helper/image.html
new file mode 100644
index 0000000..308a36c
--- /dev/null
+++ b/layouts/partials/helper/image.html
@@ -0,0 +1,2 @@
+{{- $image := .Resources.GetMatch (printf "%s" (.Params.image | safeURL)) -}}
+{{ return $image }} \ No newline at end of file
diff --git a/layouts/partials/pagination.html b/layouts/partials/pagination.html
new file mode 100644
index 0000000..7756050
--- /dev/null
+++ b/layouts/partials/pagination.html
@@ -0,0 +1,26 @@
+{{ if gt .Paginator.TotalPages 1 }}
+ <nav class='pagination'>
+ {{ $.Scratch.Set "hasPrevDots" false }}
+ {{ $.Scratch.Set "hasNextDots" false }}
+
+ {{ range .Paginator.Pagers }}
+ {{ if eq . $.Paginator }}
+ <span class='page-link current'>
+ {{- .PageNumber -}}
+ </span>
+ {{ else if or (or (eq . $.Paginator.First) (eq . $.Paginator.Prev)) (or (eq . $.Paginator.Next) (eq . $.Paginator.Last )) }}
+ <a class='page-link' href='{{ .URL }}'>
+ {{- .PageNumber -}}
+ </a>
+ {{ else }}
+ {{ if and (not ($.Scratch.Get "hasPrevDots")) (lt .PageNumber $.Paginator.PageNumber) }}
+ {{ $.Scratch.Set "hasPrevDots" true }}
+ <span class='page-link dots'>&hellip;</span>
+ {{ else if and (not ($.Scratch.Get "hasNextDots")) (gt .PageNumber $.Paginator.PageNumber) }}
+ {{ $.Scratch.Set "hasNextDots" true }}
+ <span class='page-link dots'>&hellip;</span>
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ </nav>
+{{ end }}
diff --git a/layouts/partials/sidebar/left.html b/layouts/partials/sidebar/left.html
new file mode 100644
index 0000000..ff536a8
--- /dev/null
+++ b/layouts/partials/sidebar/left.html
@@ -0,0 +1,33 @@
+<aside class="sidebar left-sidebar sticky">
+ <button class="hamburger hamburger--spin" type="button" id="toggle-menu" aria-label="Toggle Menu">
+ <span class="hamburger-box">
+ <span class="hamburger-inner"></span>
+ </span>
+ </button>
+
+ <header class="site-info">
+ <figure class="site-avatar">
+ {{ $avatar := resources.Get (.Site.Params.avatar) }}
+ {{ $avatarResized := $avatar.Resize "300x300" }}
+ <img src="{{ $avatarResized.RelPermalink }}" width="{{ $avatarResized.Width }}"
+ height="{{ $avatarResized.Height }}" class="site-logo" loading="lazy" alt="Avatar">
+ <span class="emoji">{{ .Site.Params.emoji }}</span>
+ </figure>
+ <h1 class="site-name"><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h1>
+ <h2 class="site-description">{{ .Site.Params.subtitle }}</h2>
+ </header>
+
+ <nav class="menu" id="main-menu">
+ {{ $currentPage := . }}
+ {{ range .Site.Menus.main }}
+ {{ $active := or (eq $currentPage.Title .Name) (or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .)) }}
+
+ <li {{ if $active }} class='current' {{ end }}>
+ <a href='{{ .URL }}'>
+ {{ (resources.Get (delimit (slice "icons/" .Pre ".svg") "")).Content | safeHTML }}
+ <span>{{- .Name -}}</span>
+ </a>
+ </li>
+ {{ end }}
+ </nav>
+</aside> \ No newline at end of file
diff --git a/layouts/partials/sidebar/right.html b/layouts/partials/sidebar/right.html
new file mode 100644
index 0000000..24763bb
--- /dev/null
+++ b/layouts/partials/sidebar/right.html
@@ -0,0 +1,8 @@
+{{ if .Site.Params.widgets.enabled }}
+ {{ $context := . }}
+ <aside class="sidebar right-sidebar sticky">
+ {{ range $widget := .Site.Params.widgets.enabled }}
+ {{ partial (printf "widget/%s" $widget) $context }}
+ {{ end }}
+ </aside>
+{{ end }} \ No newline at end of file
diff --git a/layouts/partials/widget/archive.html b/layouts/partials/widget/archive.html
new file mode 100644
index 0000000..cfe02df
--- /dev/null
+++ b/layouts/partials/widget/archive.html
@@ -0,0 +1,19 @@
+<section class="widget archive">
+ <div class="widget-icon">
+ {{ (resources.Get "icons/infinity.svg").Content | safeHTML }}
+ </div>
+ <h1 class="widget-title">Archive</h1>
+
+ {{ $v1 := where .Site.RegularPages "Section" "post" }}
+ {{ $v2 := where .Site.RegularPages "Params.hidden" "!=" true }}
+ {{ $filtered := $v1 | intersect $v2 }}
+ {{ range $filtered.GroupByDate "2006" }}
+ {{ $id := lower (replace .Key " " "-") }}
+ <div class="archive-year">
+ <a href="{{ $.Site.BaseURL }}/archive#{{ $id }}">
+ <span class="year">{{ .Key }}</span>
+ <span class="count">{{ len .Pages }}</span>
+ </a>
+ </div>
+ {{ end }}
+</section>
diff --git a/layouts/partials/widget/tag-cloud.html b/layouts/partials/widget/tag-cloud.html
new file mode 100644
index 0000000..1dcddfc
--- /dev/null
+++ b/layouts/partials/widget/tag-cloud.html
@@ -0,0 +1,17 @@
+{{ $tags := .Site.Taxonomies.tags.ByCount }}
+{{ $v2 := where $tags "Term" "not in" (slice "hugo" "tag" "rss") }}
+
+<section class="widget tagCloud">
+ <div class="widget-icon">
+ {{ (resources.Get "icons/tag.svg").Content | safeHTML }}
+ </div>
+ <h1 class="widget-title">Tags</h1>
+
+ <div class="tagCloud-tags">
+ {{ range first .Site.Params.widgets.tagCloud.limit $v2 }}
+ <a href="{{ $.Site.BaseURL }}tags/{{ .Term | urlize }}/" class="font_size_{{ .Count }}">
+ {{ .Term | humanize }}
+ </a>
+ {{ end }}
+ </div>
+</section> \ No newline at end of file
diff --git a/layouts/shortcodes/youtube.html b/layouts/shortcodes/youtube.html
new file mode 100644
index 0000000..5dd94e6
--- /dev/null
+++ b/layouts/shortcodes/youtube.html
@@ -0,0 +1,9 @@
+{{- $pc := .Page.Site.Config.Privacy.YouTube -}}
+{{- if not $pc.Disable -}}
+{{- $ytHost := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" -}}
+{{- $id := .Get "id" | default (.Get 0) -}}
+{{- $class := .Get "class" | default (.Get 1) }}
+<div {{ with $class }}class="{{ . }}"{{ else }}style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"{{ end }}>
+ <iframe loading="lazy" src="https://{{ $ytHost }}/embed/{{ $id }}{{ with .Get "autoplay" }}{{ if eq . "true" }}?autoplay=1{{ end }}{{ end }}" {{ if not $class }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" {{ end }}allowfullscreen title="YouTube Video"></iframe>
+</div>
+{{ end -}} \ No newline at end of file
diff --git a/theme.toml b/theme.toml
new file mode 100644
index 0000000..69192aa
--- /dev/null
+++ b/theme.toml
@@ -0,0 +1,15 @@
+# theme.toml template for a Hugo theme
+# See https://github.com/gohugoio/hugoThemes#themetoml for an example
+
+name = "Stack"
+license = "GPL-3.0-only"
+licenselink = "https://github.com/CaiJimmy/hugo-theme-stack/blob/master/LICENSE"
+description = ""
+homepage = "https://blog.jimmycai.com/p/hugo-theme-stack"
+tags = []
+features = []
+min_version = "0.74.0"
+
+[author]
+ name = "Jimmy Cai"
+ homepage = "https://jimmycai.com" \ No newline at end of file