diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/LICENSE-BOOTSTRAP.md b/.github/LICENSE-BOOTSTRAP.md new file mode 100644 index 0000000..6ed32f9 --- /dev/null +++ b/.github/LICENSE-BOOTSTRAP.md @@ -0,0 +1,7 @@ +Copyright 2017 - Present Stuart Yamartino (@StuYam) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9dd8f76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/_site +/.sass-cache +.DS_Store diff --git a/404.html b/404.html new file mode 100644 index 0000000..c472b4e --- /dev/null +++ b/404.html @@ -0,0 +1,24 @@ +--- +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..9a75399 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +converged-computing.org diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..dbbfc4b --- /dev/null +++ b/Gemfile @@ -0,0 +1,21 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! +# gem 'jekyll', '>= 3.6.3' + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins +gem "webrick" + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem 'jekyll-feed', '~> 0.6' +end diff --git a/README.md b/README.md index fc9b407..ee4309e 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,13 @@ > A Best-of-Both-Worlds of HPC and Cloud -This is a [CiSE Special Issue Proposal](https://www.authorea.com/users/34995/articles/430859-cise-guest-editors-guide) and -the repository where we will write and document our whitepaper to introduce it. - -## Important Dates - -- Anticipated for 2023-2023 +This is the Converged Computing working space. It includes definitions, projects, and eventually will also include a [CiSE Special Issue](https://www.authorea.com/users/34995/articles/430859-cise-guest-editors-guide) on converged computing. ## Overview -Major trends have brought the cloud and high performance computing (HPC) communities closer together: the maturation of cloud technologies has shifted focus toward running workloads efficiently, and composite scientific workflows and resource heterogeneity and dynamism have revealed the limitations of traditional HPC resource and workflow management. It has become clear that the initially disparate communities have much to benefit in working together. This special issue aims to bridge the gap between HPC and cloud to discuss work in converged computing – the collaborative space between these traditionally separate communities – to develop novel technologies and applications. These hybrid technologies might span the gamut from automation, workflows, containerization, to software development and deployment and testing. This is a timely topic as collaborative work is happening to a greater degree that combines approaches from high performance computing with cloud-native computing. +> Special Issue Anticipated for mid 2024 +Major trends have brought the cloud and high performance computing (HPC) communities closer together: the maturation of cloud technologies has shifted focus toward running workloads efficiently, and composite scientific workflows and resource heterogeneity and dynamism have revealed the limitations of traditional HPC resource and workflow management. It has become clear that the initially disparate communities have much to benefit in working together. This special issue aims to bridge the gap between HPC and cloud to discuss work in converged computing – the collaborative space between these traditionally separate communities – to develop novel technologies and applications. These hybrid technologies might span the gamut from automation, workflows, containerization, to software development and deployment and testing. This is a timely topic as collaborative work is happening to a greater degree that combines approaches from high performance computing with cloud-native computing. ## Guest Editors @@ -24,3 +20,7 @@ Major trends have brought the cloud and high performance computing (HPC) communi - Jakob Luettgau, University of Tennessee Knoxville - Evan Bollig, Amazon Web Services - Bill Magro, Google + +## Thank you + +This site is based on [this template](https://bootstrapemail.com/) that is covered under the MIT license. The license is included [here](.github/LICENSE-BOOTSTRAP.md). diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..a126725 --- /dev/null +++ b/_config.yml @@ -0,0 +1,53 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. + +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: Converged Computing +email: sochat1@llnl.gov +description: >- # this means to ignore newlines until "baseurl:" + The best of both worlds for cloud and HPC +baseurl: "" # the subpath of your site, e.g. /blog +url: "https://converged-computing.org" # the base hostname & protocol for your site, e.g. http://example.com +permalink: /docs/:title + +# Build settings +markdown: kramdown +plugins: + - jekyll-feed +kramdown: + auto_id_prefix: 'doc-' + +# Collections +collections: + docs: + output: true + permalink: /:collection/:title/ + +# Defaults +defaults: + - + scope: + path: "" + type: "docs" + values: + layout: "docs" + + +# Exclude from processing. +# The following items will not be processed, by default. Create a custom list +# to override the default setting. +exclude: + - Gemfile + - Gemfile.lock + - vendor \ No newline at end of file diff --git a/_data/border_radiuses.csv b/_data/border_radiuses.csv new file mode 100644 index 0000000..25556ce --- /dev/null +++ b/_data/border_radiuses.csv @@ -0,0 +1,10 @@ +name,radius +,4 +-none,0 +-sm,2 +-md,6 +-lg,8 +-xl,12 +-2xl,16 +-3xl,24 +-full,9999 diff --git a/_data/border_widths.csv b/_data/border_widths.csv new file mode 100644 index 0000000..5870a9c --- /dev/null +++ b/_data/border_widths.csv @@ -0,0 +1,7 @@ +name,width +,1 +-2,2 +-3,3 +-4,4 +-5,5 +-0,0 diff --git a/_data/displays.json b/_data/displays.json new file mode 100644 index 0000000..87a5bfa --- /dev/null +++ b/_data/displays.json @@ -0,0 +1 @@ +['inline','inline-block','block','table','none'] diff --git a/_data/font_sizes.csv b/_data/font_sizes.csv new file mode 100644 index 0000000..dfc8ffb --- /dev/null +++ b/_data/font_sizes.csv @@ -0,0 +1,12 @@ +name,size +xs,12 +sm,14 +base,16 +lg,18 +xl,20 +2xl,24 +3xl,30 +4xl,36 +5xl,48 +6xl,64 +7xl,80 diff --git a/_data/font_weights.json b/_data/font_weights.json new file mode 100644 index 0000000..fdf2c27 --- /dev/null +++ b/_data/font_weights.json @@ -0,0 +1 @@ +[100, 200, 300, 400, 500, 600, 700, 800, 900] diff --git a/_data/grid_cols.csv b/_data/grid_cols.csv new file mode 100644 index 0000000..848e3b6 --- /dev/null +++ b/_data/grid_cols.csv @@ -0,0 +1,13 @@ +name,width +1,8.333333% +2,16.666667% +3,25% +4,33.333333% +5,41.666667% +6,50% +7,58.333333% +8,66.666667% +9,75% +10,83.333333% +11,91.666667% +12,100% diff --git a/_data/line_heights.csv b/_data/line_heights.csv new file mode 100644 index 0000000..37a423b --- /dev/null +++ b/_data/line_heights.csv @@ -0,0 +1,5 @@ +name,size +1,1 +sm,1.25 +base,1.5 +lg,2 diff --git a/_data/palette_colors.csv b/_data/palette_colors.csv new file mode 100644 index 0000000..54085b4 --- /dev/null +++ b/_data/palette_colors.csv @@ -0,0 +1,103 @@ +name,color +black,#000000 +white,#ffffff +transparent,transparent +gray-100,#f8f9fa +gray-200,#e9ecef +gray-300,#dee2e6 +gray-400,#ced4da +gray-500,#adb5bd +gray-600,#6c757d +gray-700,#495057 +gray-800,#343a40 +gray-900,#212529 +blue-100,#cfe2ff +blue-200,#9ec5fe +blue-300,#6ea8fe +blue-400,#3d8bfd +blue-500,#0d6efd +blue-600,#0a58ca +blue-700,#084298 +blue-800,#052c65 +blue-900,#031633 +indigo-100,#e0cffc +indigo-200,#c29ffa +indigo-300,#a370f7 +indigo-400,#8540f5 +indigo-500,#6610f2 +indigo-600,#520dc2 +indigo-700,#3d0a91 +indigo-800,#290661 +indigo-900,#140330 +purple-100,#e2d9f3 +purple-200,#c5b3e6 +purple-300,#a98eda +purple-400,#8c68cd +purple-500,#6f42c1 +purple-600,#59359a +purple-700,#432874 +purple-800,#2c1a4d +purple-900,#160d27 +pink-100,#f7d6e6 +pink-200,#efadce +pink-300,#e685b5 +pink-400,#de5c9d +pink-500,#d63384 +pink-600,#ab296a +pink-700,#801f4f +pink-800,#561435 +pink-900,#2b0a1a +red-100,#f8d7da +red-200,#f1aeb5 +red-300,#ea868f +red-400,#e35d6a +red-500,#dc3545 +red-600,#b02a37 +red-700,#842029 +red-800,#58151c +red-900,#2c0b0e +orange-100,#ffe5d0 +orange-200,#fecba1 +orange-300,#feb272 +orange-400,#fd9843 +orange-500,#fd7e14 +orange-600,#ca6510 +orange-700,#984c0c +orange-800,#653208 +orange-900,#331904 +yellow-100,#fff3cd +yellow-200,#ffe69c +yellow-300,#ffda6a +yellow-400,#ffcd39 +yellow-500,#ffc107 +yellow-600,#cc9a06 +yellow-700,#997404 +yellow-800,#664d03 +yellow-900,#332701 +green-100,#d1e7dd +green-200,#a3cfbb +green-300,#75b798 +green-400,#479f76 +green-500,#198754 +green-600,#146c43 +green-700,#0f5132 +green-800,#0a3622 +green-900,#051b11 +teal-100,#d2f4ea +teal-200,#a6e9d5 +teal-300,#79dfc1 +teal-400,#4dd4ac +teal-500,#20c997 +teal-600,#1aa179 +teal-700,#13795b +teal-800,#0d503c +teal-900,#06281e +cyan-100,#cff4fc +cyan-200,#9eeaf9 +cyan-300,#6edff6 +cyan-400,#3dd5f3 +cyan-500,#0dcaf0 +cyan-600,#0aa2c0 +cyan-700,#087990 +cyan-800,#055160 +cyan-900,#032830 diff --git a/_data/sizings.json b/_data/sizings.json new file mode 100644 index 0000000..bc0789d --- /dev/null +++ b/_data/sizings.json @@ -0,0 +1 @@ +[0,1,2,3,4,5,6,7,8,9,10,12,16,20,24,32,40,48,56,64,80,96,112,128,144,150] diff --git a/_data/spacings.json b/_data/spacings.json new file mode 100644 index 0000000..c966fce --- /dev/null +++ b/_data/spacings.json @@ -0,0 +1 @@ +[0,1,2,3,4,5,6,7,8,9,10,12,16,20,24,32,40] diff --git a/_data/terms.yaml b/_data/terms.yaml new file mode 100644 index 0000000..3f91d97 --- /dev/null +++ b/_data/terms.yaml @@ -0,0 +1,24 @@ +getting-started: +- name: Introduction + url: /docs/introduction +- name: About + url: /about +# - name: Goals +# url: /docs/goals +comparisons: +- name: HPC vs HTC + url: /docs/hpc-vs-htc + +patterns: +- name: jobs + url: /docs/job-patterns +- name: workflows + url: /docs/workflow-patterns +#- section: Projects +# items: +# - name: Journal Special Issue +# url: /docs/journal +# - name: Kubernetes +# url: /docs/kubernetes +# - name: HPC Schedulers +# url: /docs/hpc diff --git a/_data/theme_colors.csv b/_data/theme_colors.csv new file mode 100644 index 0000000..beaad4a --- /dev/null +++ b/_data/theme_colors.csv @@ -0,0 +1,9 @@ +name,color +primary,#0d6efd +secondary,#6c757d +success,#198754 +info,#0dcaf0 +warning,#ffc107 +danger,#dc3545 +light,#f8f9fa +dark,#212529 diff --git a/_docs/accounting.md b/_docs/accounting.md new file mode 100644 index 0000000..4009ada --- /dev/null +++ b/_docs/accounting.md @@ -0,0 +1,14 @@ +--- +layout: docs +title: "Accounting" +sections: + - Example +--- + +Keeping track of resource usage time, usually on the level of a user or group. This information can be stored in a database and used for making future decisions about scheduling or priority. In terms of units of resource usage, this can vary. + +## Example + +HPC uses core-hours (time spent doing the compute). One core-hour equals one CPU core being used for the duration of one hour of execution time. The time is measured as the jobs elapsed wall-clock time from start to finish. [[ref](https://help.itc.rwth-aachen.de/en/service/rhr4fjjutttf/article/090b27dc31484f3c833957978b039b55/), [ref](https://slurm.schedmd.com/accounting.html)]. Cloud does not have the same need for multi-tenancy as HPC, and so accounting to track resource usage is typically associated with cost. Cloud accounts for resources to bill projects, and charges based on data usage or instance cost. + +In Flux, accounting is separate from scheduling (flux-sched) as a design choice to make them swappable (e.g., install a different scheduler) however in many HPC managers they are built together, meaning accounting is part of the scheduler. diff --git a/_docs/allocation.md b/_docs/allocation.md new file mode 100644 index 0000000..3ef6a6c --- /dev/null +++ b/_docs/allocation.md @@ -0,0 +1,12 @@ +--- +layout: docs +title: "Allocation" +#sections: +# - Example +--- + +The actual set of physical resources that an application is running on. There are two types of allocations: shared and exclusive. + + - *Exclusive allocation*: jobs have dedicated physical resources (e.g., nodes, cores, memory). Multiple users cannot share a physical resource, and resources are not shared between jobs. This setup is common in HPC. + - *Shared allocation*: jobs share underlying physical resources (e.g., nodes, cores, memory). Multiple users can run on the same physical resources. This setup is common in cloud. + diff --git a/_docs/application-programming-interface.md b/_docs/application-programming-interface.md new file mode 100644 index 0000000..d298dff --- /dev/null +++ b/_docs/application-programming-interface.md @@ -0,0 +1,8 @@ +--- +layout: docs +title: "Application Programming Interface" +#sections: +# - Example +--- + +Programmatic interface for interacting with components. In Kubernetes this might be the kube-apiserver, and in Flux it could be a socket (connected via flux proxy) or the flux restful API. diff --git a/_docs/batch-system.md b/_docs/batch-system.md new file mode 100644 index 0000000..63b155b --- /dev/null +++ b/_docs/batch-system.md @@ -0,0 +1,18 @@ +--- +layout: docs +title: "Batch System" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +A batch system provides the interface to manage resources and schedule workloads. Due to the complexity, batch systems often manifest as a combination of components or modules, and we can see this for both HPC systems and Kubernetes. + +Advanced / developer note: the modularity afforded by these systems often makes them very flexible / pluggable for integrating components from one into the other, etc. Batch systems can be nested in other batch systems, if the technology allows for it. + + +{% include example.html prefix="Flux:" text="We might consider the combination of flux-core, flux-sched, and flux-security as a combined set of modules that manifest in a batch system, deployed on either bare metal or a virtual machine (or pod)." %} + +
+ +{% include example.html prefix="Kubernetes:" text="We might consider the Kubernetes default scheduler combined with control-plane(s) and one or more kubelet a basic batch system, with the addition of Kueue to add a queue. In both cases, the batch systems can receive some specification for a unit of work (e.g., a job)." %} diff --git a/_docs/batch.md b/_docs/batch.md new file mode 100644 index 0000000..cbfbe24 --- /dev/null +++ b/_docs/batch.md @@ -0,0 +1,9 @@ +--- +layout: docs +title: "Batch" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +When someone says "batch" they are very generally referring to the combined system to manage resources and schedule workloads. The root of the term originates from "batch processing" - a sequence of programs and input data that would be processed in serial on a computer [[ref](https://en.wikipedia.org/wiki/Batch_processing)]. In Kubernetes, batch refers to the [working group](https://github.com/kubernetes/community/blob/master/wg-batch/README.md) and set of APIs (Job and CronJob abstractions) concerned with submitting jobs, which are fundamentally different than other Kubernetes objects because they have state, and are intended to be created and at some point complete. There is also a Cloud Native Compuing Foundation (CNCF) [batch working group](https://tag-runtime.cncf.io/wgs/bsi/charter/) that is interested in similar abstractions, and takes a stronger research standpoint. A very simple idea of "batch" (often used in Kubernetes) is to describe jobs running in parallel. This can be done both for Kubernetes and HPC. However, the coupling of the jobs is often very different between the spaces, with HPC being tightly coupled and Kubernetes batch more loosely. These ideas can be further expanded later. diff --git a/_docs/cluster.md b/_docs/cluster.md new file mode 100644 index 0000000..079b531 --- /dev/null +++ b/_docs/cluster.md @@ -0,0 +1,12 @@ +--- +layout: docs +title: "Cluster" +sections: + - Example +--- + +A cluster is a group of resources that collectively run applications or provide services or storage. Clusters exist to serve the needs of multiple users and/or multiple applications with resource requirements that are greater than what is provided by a single machine. The complexity of the cluster generally reflects the needs of its users, and often reflects the economic decisions of a center providing the resource. + +## Example + +As an example, owning a cluster (common in HPC communities) presents a different cost model than renting resources (common for cloud) that providers/centers must choose between. The nodes can be co-located (close by in terms of communication latency) or far apart (regions or zones in a cloud provider). Generally speaking, co-location can have a huge influence on network latency and thus application performance. If we consider all the compute in the world, we can consider a cluster akin to a cluster in a graph - a set of vertices that are more highly connected to one another than other nodes. A cluster could be a set of nodes running a resource manager on bare metal of an HPC cluster, or a set of nodes running Kubernetes and applications via pods on those nodes. While cloud and HPC are predominant now, the first distributed clusters came by way of grid computing in the early 1990s. diff --git a/_docs/compute.md b/_docs/compute.md new file mode 100644 index 0000000..b4ec798 --- /dev/null +++ b/_docs/compute.md @@ -0,0 +1,14 @@ +--- +layout: docs +title: "Compute" +sections: + - Example +# - Why do we need to work together +--- + +Computing is changing information (i.e., bits) in time. Compute resources can be connected to each other via a network, and can transmit the results of computation to storage. + +## Example + +A computing processor such as a general-purpose CPU executes instructions to transform bits. The rate at which instructions can transform bits is a fundamental measure of computing performance. Specialized computing processors such as GPUs, FPGAs, ASICs, and others are designed to sacrifice general-purpose performance for higher performance on more specialized instructions or types of bit transformations. Access to computing resources can be physical or virtual. + diff --git a/_docs/database.md b/_docs/database.md new file mode 100644 index 0000000..7676a9a --- /dev/null +++ b/_docs/database.md @@ -0,0 +1,14 @@ +--- +layout: docs +title: "Database" +sections: + - Example +--- + +An organized collection of data, typically optimized to support receiving or querying certain information efficiently. + +## Example + +Any kind of key value store (e.g., etcd in Kubernetes, or kvs the "key value store" in Flux) that can store some component of state for a cluster, resource, or application. + + diff --git a/_docs/hpc-vs-htc.md b/_docs/hpc-vs-htc.md new file mode 100644 index 0000000..26a18cc --- /dev/null +++ b/_docs/hpc-vs-htc.md @@ -0,0 +1,17 @@ +--- +layout: docs +title: "HPC vs HTC" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +Although HPC is mentioned heavily within k8s-batch, HTC is not covered as extensively. + +HPC, or "High Performance Computing," refers to the deployment and use of supercomputers and parallel processing techniques for solving complex computational problems. HPC systems are designed to deliver high performance by aggregating computing power in a way that enables higher processing speed and throughput for tasks that require significant computational resources. + +Whereas HTC, or "High Throughput Computing," focuses on the efficient execution of a large number of loosely coupled or independent tasks over long periods of time. HTC systems are optimized not for the raw speed of any single computation, but for the overall volume of computations that can be performed over a given time period. + +K8s is not constructed for HTC workloads, and there are [no plans to support it natively](https://github.com/kubernetes-sigs/kueue/blob/main/keps/693-multikueue/README.md?plain=1#L56). The main players trying to support k8s-HTC are [HTCondor](https://htcondor.org/) (both in k8s and out) as well as [Armada](https://github.com/armadaproject/armada). + +There is also MTC, “Many Task Computing”, which tries to bridge the two, but that term is used significantly less. See these [notes](https://en.wikipedia.org/wiki/High-throughput_computing#High-throughput_vs._high-performance_vs._many-task) on Wikipedia to learn more. diff --git a/_docs/introduction.md b/_docs/introduction.md new file mode 100644 index 0000000..eb5e084 --- /dev/null +++ b/_docs/introduction.md @@ -0,0 +1,23 @@ +--- +layout: docs +title: "Introduction" +sections: + - What is Converged Computing + - Why do we need to work together +--- + +### What is Converged Computing? + +We have vision for a future where the cloud and high performance computing communities are working together, and learning from one another to build new technologies that combine the best of both worlds. If you think about it, the area between cloud and HPC is muddled at best, akin to a “fog of war” in your favorite computer game. We aim to shed some light on this fog, and map out the landscape that will make it possible to easily move workloads between cloud and HPC environments. We plan to tackle issues that range from: + +- job orchestration and management +- resource management +- scheduling +- ensembles + +and more! This is a living document that should be updated when needed and represent a current state of thinking. + +### Why do we need to work together? + +This is a collaboration between the HPC and cloud native (industry) communities to derive definitions for converged computing. We are starting with high level definitions that will populate a website on converged computing (hosted at our converged-computing GitHub, branded professionally) and associated videos on YouTube. We will present the landscape of these terms, first at a high level, and then drill down into each with details about what it means for HPC vs. Kubernetes. This work is an important collaborative effort to present definitions and concepts that account for the experience of both communities! + diff --git a/_docs/job-manager.md b/_docs/job-manager.md new file mode 100644 index 0000000..bdbd5b4 --- /dev/null +++ b/_docs/job-manager.md @@ -0,0 +1,8 @@ +--- +layout: docs +title: "Job Manager" +#sections: +# - Example +--- + +The job manager receives new jobs, and is in charge of moving them through states. As an example, the Flux Framework job manager defines states new, depend, priority, sched, run, and cleanup. Each state might trigger events or connections to other components of the batch system. diff --git a/_docs/job.md b/_docs/job.md new file mode 100644 index 0000000..18e1f02 --- /dev/null +++ b/_docs/job.md @@ -0,0 +1,11 @@ +--- +layout: docs +title: "Job" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +A job is a unit of work that runs to completion, and is typically the combination of input data, environment variables, description of resources needed, an application, and output directives. On a high level a single job can be replicated into an array (multiple of the same job run in parallel) or assembled like legos into a workflow to manifest in more complex application logic. It's common to refer to a grouping of jobs, where they are non-interactive and submitting to use a complex set of resources, as a batch job. A job is typically akin to one step or task in a workflow, or run on its own. + +{% include example.html text="In Flux, a batch job can be defined by way of a script that can create one or more flux jobs or even allocations. In Kubernetes, a batch job could be a Job, or a JobSet." %} \ No newline at end of file diff --git a/_docs/jobspec.md b/_docs/jobspec.md new file mode 100644 index 0000000..4986c58 --- /dev/null +++ b/_docs/jobspec.md @@ -0,0 +1,9 @@ +--- +layout: docs +title: "Job Specification" +#sections: +# - Example +--- + +An abstraction that describes the resources, applications, task configuration, and amount of time needed by a job. This includes (on a high level) everything from executables to environment to resources needed. In Kubernetes a jobspec might look like a YAML specification for a pod or batch/v1 job, and on a traditional HPC system it might look like a bash script that runs an executable with directives for resources and other parameters. Flux has a form definition of a JobSpec that populates a resource graph. + diff --git a/_docs/monitoring.md b/_docs/monitoring.md new file mode 100644 index 0000000..a2c364c --- /dev/null +++ b/_docs/monitoring.md @@ -0,0 +1,8 @@ +--- +layout: docs +title: "Monitoring" +#sections: +# - Example +--- + +Tools and methods to keep track of the status/state of a system and/or applications running on it. Likely, strategies for monitoring of systems are different from those that monitor applications. This definition might be handled based on "SRE" type questions - what happens when a node goes down? A node vs. a pod vs. an application? What happens if there is a security vulnerability? diff --git a/_docs/network.md b/_docs/network.md new file mode 100644 index 0000000..d7c3adf --- /dev/null +++ b/_docs/network.md @@ -0,0 +1,15 @@ +--- +layout: docs +title: "Network" +sections: + - Example +# - Why do we need to work together +--- + +Networking is transportation of bits through space. A network should support connectivity of nodes (and components within) and ideally have low latency, high bandwidth, high reliability, and concurrent connections. The needs of the network should support the needs of the applications that users want to run. + +## Example + +In high performance computing a network might be an Infiniband fabric, and in a simple local network you might just have ethernet, or on cloud something like elastic fiber adapted (EFA on AWS). Often networks are optimized for specific hardware resources (e.g., Google TPU) with preference placed to a particular customer base or application need. + + diff --git a/_docs/nodes.md b/_docs/nodes.md new file mode 100644 index 0000000..1ac778b --- /dev/null +++ b/_docs/nodes.md @@ -0,0 +1,19 @@ +--- +layout: docs +title: "Nodes" +sections: + - Example +--- + +> Synonyms: virtual machine (VM) instances, server + +A node is a server resource, an addressable compute or storage unit (often supporting an ip-address). A node can either be: + +- virtual: an abstraction to virtualize system resources. A node that is virtual can be a virtual machine, which uses some hypervisor technology, or even a linux container. +- physical: no virtualization, and often called "bare metal" + +A node can exclusively be for storage or compute, or can serve both purposes. + +## Example + +A [Kubernetes node](https://kubernetes.io/docs/concepts/architecture/nodes/) is typically intended for compute, as the unit for storage would be called a [volume](https://kubernetes.io/docs/concepts/storage/). A node deployed for HPC typically takes on one role or a hybrid approach. Nodes can be shared or exclusive (see allocation type). At a high level, a "node" might refer to an instance on a cloud provider, a "node" in Kubernetes [[ref](https://kubernetes.io/docs/concepts/architecture/nodes/)], or a bare metal machine. It is an abstraction for a base machine. diff --git a/_docs/pod.md b/_docs/pod.md new file mode 100644 index 0000000..d285bb5 --- /dev/null +++ b/_docs/pod.md @@ -0,0 +1,13 @@ +--- +layout: docs +title: "Pod" +sections: + - Example +# - Why do we need to work together +--- + +A slice of a node that provides a layer of abstraction for defining a scoped piece of work, including (but not limited to) providing an operating system, or a grouping of resources. A pod typically makes a request for resources, and is intended to run one or more units of work (e.g., containers). Pods are exclusive to Kubernetes and the term is not used in HPC, however we might consider the HPC equivalent to be anything that represents a "unit of computing" that is schedulable for units of work (some number of containers or applications). + +## Example + +For Kubernetes, a unit of computing is referred to as a Pod, which explicitly is a group of containers with shared storage and network resources. A pod is a logical node. It is the smallest unit of work you can deploy. The closest thing we have in HPC would be like a scheduling script or a resource request - a packaged set of work that requires some set of resources. A key difference is that pods are designed to well support services, while batch work on HPC is not. diff --git a/_docs/queueing.md b/_docs/queueing.md new file mode 100644 index 0000000..0907b10 --- /dev/null +++ b/_docs/queueing.md @@ -0,0 +1,13 @@ +--- +layout: docs +title: "Scheduling" +sections: + - Example +--- + +A means to take requests for work and order them in a way by priority (fair share or other algorithms) and availability. Queuing systems are interested in attributes such as satisfiability, reservations, and backfilling. + +## Example + +In Kubernetes, a tool like Kueue [[ref](https://kueue.sigs.k8s.io/docs/tasks/administer_cluster_quotas/)] might create simple job requests for users and assign them to be run based on priority settings. In an HPC resource manager like SLURM [[ref](https://slurm.schedmd.com/quickstart.html)], it's more likely that groups of nodes (called partitions) are allocated to user jobs based on an algorithm like fair share. + diff --git a/_docs/reservation.md b/_docs/reservation.md new file mode 100644 index 0000000..e3d76ef --- /dev/null +++ b/_docs/reservation.md @@ -0,0 +1,8 @@ +--- +layout: docs +title: "Reservation" +#sections: +# - Example +--- + +An assurance of availability for a quantity and quality of resources for a future time-point. If resources are unavailable at this point in time for a job, we reserve an allocation (physical resources) for that job in the future. There can be variance between how the reservation is done (e.g., very specific resources vs. matching a particular flavor) as well as guarantees, as different environments make different assumptions about resource availability. In an environment where there are essentially unlimited resources, a reservation may not be a relevant or widely used term. diff --git a/_docs/resource-manager.md b/_docs/resource-manager.md new file mode 100644 index 0000000..c864912 --- /dev/null +++ b/_docs/resource-manager.md @@ -0,0 +1,8 @@ +--- +layout: docs +title: "Resource Manager" +#sections: +# - Example +--- + +The resource manager is in charge of tracking and monitoring hardware resources. diff --git a/_docs/resources.md b/_docs/resources.md new file mode 100644 index 0000000..2f65417 --- /dev/null +++ b/_docs/resources.md @@ -0,0 +1,15 @@ +--- +layout: docs +title: "Resources" +sections: + - Example +--- + + +Hardware (nodes, storage, accelerators) or flow transports (network, power) that make up a system (that can be logically partitioned for tasks) and can be consumed by its users. Each resource type has a unit of abstraction, and is finite and thus needs to be managed. + + +## Example + +Hardware typically provides compute or storage for a job, and the scale can vary from a socket or CPU to an entire node. The finite nature of resources in the context of some number of tasks or users requires intelligent scheduling. On cloud, there is a perception that resources are unlimited, and only bounded by the amount of money allowed to be spent. This is typically not the truth. For high performance computing, the entire set of resources is more transparent. In both cases, the resources are finite, however the perception is different. + diff --git a/_docs/scheduling.md b/_docs/scheduling.md new file mode 100644 index 0000000..b9a5cfe --- /dev/null +++ b/_docs/scheduling.md @@ -0,0 +1,12 @@ +--- +layout: docs +title: "Scheduling" +sections: + - Example +--- + +Taking a resource request and performing a mapping such that units of work are mapped to resources. A scheduler is a function that takes as input a triple (resource shape, requested start time, requested time interval) and outputs a four tuple (resources, start time of guarantee, end time guarantee, binding guarantee) to map the units of work to said resources. For a graph scheduler, this means that a job is translated to a resource request, and the resource request is a subgraph of the entire graph of resources known to the scheduler. If it's the case that a subgraph is mapped to a job id because of a reservation, we search a smaller search space. The unit of time is a variable here, as different schedulers will honor different units. + +## Example + +In Kubernetes, “scheduling” is the finding a node that can satisfy your resource request. Unlike in HPC, it is specific to scheduling pods (resource groups with containers) to physical nodes. In simple terms - "What runs next and where." diff --git a/_docs/services.md b/_docs/services.md new file mode 100644 index 0000000..9bac013 --- /dev/null +++ b/_docs/services.md @@ -0,0 +1,15 @@ +--- +layout: docs +title: "Services" +sections: + - Example +# - Why do we need to work together +--- + +A persistent interface that exposes a needed functionality. It is one or more processes that do not have a stop time. + +## Example + +In high performance computing, a service is typically a task manager, application programming interface, or database. HPC systems sometimes have protected or isolated environments explicitly for services. Services typically run alongside applications that interact via client APIs or web interfaces. In Kubernetes, a service describes exposing a network interface of a pod. This is a prime example of the same term meaning fundamentally different things. + + diff --git a/_docs/storage.md b/_docs/storage.md new file mode 100644 index 0000000..fc7228b --- /dev/null +++ b/_docs/storage.md @@ -0,0 +1,22 @@ +--- +layout: docs +title: "Storage" +sections: + - Example +--- + +> Synonyms: volumes + +Storage is transportation of information (i.e., bits) through time [^1]. It is a means to maintain a state of data. While both cloud and HPC provide underlying nodes for storing data, the extent to which the user is able to access and have awareness of the underlying structure varies. Generally speaking, locations vary in the following attributes: + +- Frequency of access (frequent vs. infrequent for an archive) +- Permanency of access (temporal or ephemeral for analyses vs. long term archives) +- Permissions (read, write, read/write) +- Cost of storage and operations/access + + +## Example + +For high performance computing, a shared filesystem is provided, and the shared nature is transparent. This means that users are aware of having ownership over a personal space (typically called "home" or a working space called "scratch") or a group space, and are provided tools to allow them to change how data is shared. For cloud, although data storage might have a similar design, the user is presented with a view of sole ownership of a set of locations for storing data. Associated filesystem or object buckets can vary in permissions, node access, and read/write. High performance computing will almost always have shared, often localized storage (a filesystem that supports multiple users with read-write) and in cloud this is almost an anti-pattern. The default (and easiest) setup is usually just read for a single user, and at most read for multiple. Setting up RWX is challenging. For high performance computing, we are interested in supporting a shared filesystem for several kinds of applications, and one with RWX across nodes for applications to share. + +[^1]: Feynman's Lectures in Computation (4.1 p. 95) ""Now this isn't exactly the same situation as with memory - which is like sending a message through time rather than space - but you can see the similarities. We store something in memory and at a later time we read it backout - in the interim the stored "message" is subject to noise." diff --git a/_docs/user-priority-and-fairness.md b/_docs/user-priority-and-fairness.md new file mode 100644 index 0000000..90bb82e --- /dev/null +++ b/_docs/user-priority-and-fairness.md @@ -0,0 +1,13 @@ +--- +layout: docs +title: "Application Programming Interface" +#sections: +# - Example +--- + +We don't keep a historical record, but rather give resources based on who deserves what at any particular instant. This is an instantaneous measurement rather than one tracked over time. [[ref](https://cs.stanford.edu/~matei/papers/2011/nsdi_drf.pdf)] + +An example comes from [Slurm](https://slurm.schedmd.com/priority_multifactor.html) + +Both priority and fairness impact how queueing is done. + diff --git a/_docs/workflow.md b/_docs/workflow.md new file mode 100644 index 0000000..d4c7422 --- /dev/null +++ b/_docs/workflow.md @@ -0,0 +1,17 @@ +--- +layout: docs +title: "Workflow" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +A workflow is a set of tasks with dependencies that need to be executed in a specific order for proper handling of exchanging inputs and outputs of data. Workflows typically have two components: data and applications, and workflow tools must decide the granularity at which to map tasks to execution workload units. Workflow steps can map into workloads, e.g., + +``` +workflow -> DAG -> scheduler -> workload +``` + +{% include example.html prefix="" text="A workflow tool might generate a directed acyclic graph (DAG) of individual tasks that can be handed to a scheduler." %} + + diff --git a/_docs/workload.md b/_docs/workload.md new file mode 100644 index 0000000..3ae8ead --- /dev/null +++ b/_docs/workload.md @@ -0,0 +1,9 @@ +--- +layout: docs +title: "Workload" +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +A workload is a set of jobs or tasks scheduled by a scheduler and managed by a resource manager. In the simplest terms, it could be as a single job tasked to do some computation using a defined set of resources. In more realistic terms, it is a data-intensive task that is spread across nodes and runs in parallel where each part uses a subset of CPU/GPU. A workload is not a workflow that has a directed acyclic graph (DAG) but rather the units of work in the graph. A vertex in a workflow DAG is a workload. If you find yourself saying Step A THEN B, you likely are not in a workload (step) but thinking about a workflow (DAG). diff --git a/_includes/citation.html b/_includes/citation.html new file mode 100644 index 0000000..beb172d --- /dev/null +++ b/_includes/citation.html @@ -0,0 +1,6 @@ +

+
+ +Suggested Citation: + +
Converged Computing Authors. "{{ page.title }}." Converged Computing Community Space {{ site.time | date_to_string }}, {{ site.url }}{{ page.url }} (accessed {{ site.time | date: '%d %b %y' }}). [doi]
diff --git a/_includes/close-html.html b/_includes/close-html.html new file mode 100644 index 0000000..3cb1c3f --- /dev/null +++ b/_includes/close-html.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/_includes/doc-header.html b/_includes/doc-header.html new file mode 100644 index 0000000..cc80b48 --- /dev/null +++ b/_includes/doc-header.html @@ -0,0 +1,14 @@ +
+

{{ page.title }}

+ {% if page.responsive == true %} + + + + + Responsive + + {% endif %} +
+
+ {{ page.tagline }} +
diff --git a/_includes/example.html b/_includes/example.html new file mode 100644 index 0000000..7744a48 --- /dev/null +++ b/_includes/example.html @@ -0,0 +1,6 @@ +
+
+ {% if include.prefix %}{{ include.prefix }}{% else %}Example:{% endif %} {{ include.text }} +
+
+ \ No newline at end of file diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000..97ee6ce --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,5 @@ + diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 0000000..6d3bbf6 --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,7 @@ +{% if include.hr == "true" %} +
+{% else %} +
+{% endif %} + +

{{ include.name }}

diff --git a/_includes/links.html b/_includes/links.html new file mode 100644 index 0000000..a0c7d7a --- /dev/null +++ b/_includes/links.html @@ -0,0 +1,25 @@ + {% capture the_collection %}{{page.collection}}{% endcapture %} + {% if page.collection %} + {% assign document = site[the_collection] %} + {% endif %} + + {% for links in document %} + {% if links.url == page.url %} + {% unless forloop.first %} + {% assign prevurl = prev.url %} + {% assign prevtitle = prev.title %} + {% endunless %} + {% unless forloop.last %} + {% assign next = document[forloop.index] %} + {% assign nexturl = next.url %} + {% assign nexttitle = next.title %} + {% endunless %} + {% endif %} + {% assign prev = links %} + {% endfor %} + {% if prevurl or nexturl %} + + {% endif %} diff --git a/_includes/navbar.html b/_includes/navbar.html new file mode 100644 index 0000000..e8aeb60 --- /dev/null +++ b/_includes/navbar.html @@ -0,0 +1,42 @@ + diff --git a/_includes/open-html.html b/_includes/open-html.html new file mode 100644 index 0000000..b38578c --- /dev/null +++ b/_includes/open-html.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + {{ site.title }} + + + diff --git a/_includes/permalinks.html b/_includes/permalinks.html new file mode 100644 index 0000000..5f94d8d --- /dev/null +++ b/_includes/permalinks.html @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/_layouts/docs.html b/_layouts/docs.html new file mode 100644 index 0000000..71b0171 --- /dev/null +++ b/_layouts/docs.html @@ -0,0 +1,53 @@ +{% include open-html.html %} +{% include navbar.html active='documentation' %} +
+
+
+ + +
+
+
+
+ {% include doc-header.html %} + {{ content }} + {% include links.html %} + {% include citation.html %} +
+
+
+
+ {% if page.sections.size > 0 %} + + {% endif %} +
+
+
+{% include footer.html %} +{% include permalinks.html %} +{% include close-html.html %} diff --git a/_layouts/main.html b/_layouts/main.html new file mode 100644 index 0000000..02eaa07 --- /dev/null +++ b/_layouts/main.html @@ -0,0 +1,5 @@ +{% include open-html.html %} +{% include navbar.html %} + {{ content }} +{% include footer.html %} +{% include close-html.html %} diff --git a/css/cssdevices.css b/css/cssdevices.css new file mode 100644 index 0000000..0337027 --- /dev/null +++ b/css/cssdevices.css @@ -0,0 +1,2 @@ +/*! CSSDevices v2.2.0 | MIT license | Maintained by Stuart Yamartino | http://cssdevices.io */ +.cd-iphone-5s,.cd-iphone-5c,.cd-iphone-5s>.cd-body,.cd-iphone-5c>.cd-body{width:19.8em;height:42em}.cd-iphone-6,.cd-iphone-6>.cd-body{width:23em;height:48em}.cd-iphone-6-plus,.cd-iphone-6-plus>.cd-body{width:25.5em;height:53em}[class^='cd-iphone'] .cd-body{position:relative;border-style:solid;background:#1e1e1e}[class^='cd-iphone-5'] .cd-body{border-radius:2.5em;border-width:.25em}[class^='cd-iphone-5'] .cd-camera{background:#3c3d3d;top:1.7em;left:50%;margin-left:-.25em;width:.5em;height:.5em;border-radius:.25em}[class^='cd-iphone-5'] .cd-ear{background:#292728;top:3em;left:50%;margin-left:-1.75em;width:3.5em;height:.6em;border-radius:.3em}[class^='cd-iphone-5'] .cd-screen{background:black;top:5em;left:50%;margin-left:-8.9em;width:17.8em;height:31em;border:solid .2em black;border-radius:.1em}[class^='cd-iphone-5'] .cd-home{bottom:1em;left:50%;margin-left:-1.75em;width:3.5em;height:3.5em;border-radius:1.75em;border:.2em solid black}[class^='cd-iphone-5'] .cd-sound{top:5.1em;left:-.35em;height:1.8em;width:.2em}[class^='cd-iphone-5'] .cd-sound::before{content:"";top:4em;height:1.4em;width:.2em}[class^='cd-iphone-5'] .cd-sound::after{content:"";top:7.2em;height:1.4em;width:.2em}[class^='cd-iphone-5'] .cd-sleep{top:-.35em;left:13.2em;height:.2em;width:3em}.cd-iphone-5c .cd-home{background:#242324;border-width:0 !important}.cd-iphone-5c .cd-home::after{content:"";display:block;width:1.2em;height:1.2em;top:1.1em;left:50%;margin-left:-.6em;border-radius:.3em;border:1px solid #393839}.cd-iphone-5c .cd-body{border-color:#7ec4fc}.cd-iphone-5c.cd-green .cd-body{border-color:#b3f390}.cd-iphone-5c.cd-red .cd-body{border-color:#fc828d}.cd-iphone-5c.cd-yellow .cd-body{border-color:#fff38a}.cd-iphone-5c.cd-white .cd-body{border-color:#efefee}.cd-iphone-5c .cd-sound,.cd-iphone-5c .cd-sound::before,.cd-iphone-5c .cd-sound::after,.cd-iphone-5c .cd-sleep{background:#7ec4fc}.cd-iphone-5c.cd-green .cd-sound,.cd-iphone-5c.cd-green .cd-sound::before,.cd-iphone-5c.cd-green .cd-sound::after,.cd-iphone-5c.cd-green .cd-sleep{background:#b3f390}.cd-iphone-5c.cd-red .cd-sound,.cd-iphone-5c.cd-red .cd-sound::before,.cd-iphone-5c.cd-red .cd-sound::after,.cd-iphone-5c.cd-red .cd-sleep{background:#fc828d}.cd-iphone-5c.cd-yellow .cd-sound,.cd-iphone-5c.cd-yellow .cd-sound::before,.cd-iphone-5c.cd-yellow .cd-sound::after,.cd-iphone-5c.cd-yellow .cd-sleep{background:#fff38a}.cd-iphone-5c.cd-white .cd-sound,.cd-iphone-5c.cd-white .cd-sound::before,.cd-iphone-5c.cd-white .cd-sound::after,.cd-iphone-5c.cd-white .cd-sleep{background:#efefee}.cd-iphone-5s .cd-body,.cd-iphone-6 .cd-body,.cd-iphone-6-plus .cd-body{border-color:#656565}.cd-iphone-5s.cd-gold .cd-body,.cd-iphone-5s.cd-gold .cd-body .cd-home,.cd-iphone-6.cd-gold .cd-body,.cd-iphone-6.cd-gold .cd-body .cd-home,.cd-iphone-6-plus.cd-gold .cd-body,.cd-iphone-6-plus.cd-gold .cd-body .cd-home{background-color:#fafafa;border-color:#ecdcc8}.cd-iphone-5s.cd-rosegold .cd-body,.cd-iphone-5s.cd-rosegold .cd-body .cd-home,.cd-iphone-6.cd-rosegold .cd-body,.cd-iphone-6.cd-rosegold .cd-body .cd-home,.cd-iphone-6-plus.cd-rosegold .cd-body,.cd-iphone-6-plus.cd-rosegold .cd-body .cd-home{background-color:#fafafa;border-color:#E9C9C5}.cd-iphone-5s.cd-silver .cd-body,.cd-iphone-5s.cd-silver .cd-body .cd-home,.cd-iphone-6.cd-silver .cd-body,.cd-iphone-6.cd-silver .cd-body .cd-home,.cd-iphone-6-plus.cd-silver .cd-body,.cd-iphone-6-plus.cd-silver .cd-body .cd-home{background-color:#fafafa;border-color:#bdbfbe}.cd-iphone-5s .cd-body .cd-home,.cd-iphone-6 .cd-body .cd-home,.cd-iphone-6-plus .cd-body .cd-home{border-color:#2c2b2c}.cd-iphone-5s .cd-sound,.cd-iphone-5s .cd-sound::before,.cd-iphone-5s .cd-sound::after,.cd-iphone-5s .cd-sleep,.cd-iphone-6 .cd-sound,.cd-iphone-6 .cd-sound::before,.cd-iphone-6 .cd-sound::after,.cd-iphone-6 .cd-sleep,.cd-iphone-6-plus .cd-sound,.cd-iphone-6-plus .cd-sound::before,.cd-iphone-6-plus .cd-sound::after,.cd-iphone-6-plus .cd-sleep{background:#656565}.cd-iphone-5s.cd-gold .cd-sound,.cd-iphone-5s.cd-gold .cd-sound::before,.cd-iphone-5s.cd-gold .cd-sound::after,.cd-iphone-5s.cd-gold .cd-sleep,.cd-iphone-6.cd-gold .cd-sound,.cd-iphone-6.cd-gold .cd-sound::before,.cd-iphone-6.cd-gold .cd-sound::after,.cd-iphone-6.cd-gold .cd-sleep,.cd-iphone-6-plus.cd-gold .cd-sound,.cd-iphone-6-plus.cd-gold .cd-sound::before,.cd-iphone-6-plus.cd-gold .cd-sound::after,.cd-iphone-6-plus.cd-gold .cd-sleep{background:#ecdcc8}.cd-iphone-5s.cd-rosegold .cd-sound,.cd-iphone-5s.cd-rosegold .cd-sound::before,.cd-iphone-5s.cd-rosegold .cd-sound::after,.cd-iphone-5s.cd-rosegold .cd-sleep,.cd-iphone-6.cd-rosegold .cd-sound,.cd-iphone-6.cd-rosegold .cd-sound::before,.cd-iphone-6.cd-rosegold .cd-sound::after,.cd-iphone-6.cd-rosegold .cd-sleep,.cd-iphone-6-plus.cd-rosegold .cd-sound,.cd-iphone-6-plus.cd-rosegold .cd-sound::before,.cd-iphone-6-plus.cd-rosegold .cd-sound::after,.cd-iphone-6-plus.cd-rosegold .cd-sleep{background:#E9C9C5}.cd-iphone-5s.cd-silver .cd-sound,.cd-iphone-5s.cd-silver .cd-sound::before,.cd-iphone-5s.cd-silver .cd-sound::after,.cd-iphone-5s.cd-silver .cd-sleep,.cd-iphone-6.cd-silver .cd-sound,.cd-iphone-6.cd-silver .cd-sound::before,.cd-iphone-6.cd-silver .cd-sound::after,.cd-iphone-6.cd-silver .cd-sleep,.cd-iphone-6-plus.cd-silver .cd-sound,.cd-iphone-6-plus.cd-silver .cd-sound::before,.cd-iphone-6-plus.cd-silver .cd-sound::after,.cd-iphone-6-plus.cd-silver .cd-sleep{background:#bdbfbe}[class^='cd-iphone-6'] .cd-body{border-radius:3em;border-width:.4em}[class^='cd-iphone-6'] .cd-camera{background:#3c3d3d;top:2.4em;left:50%;margin-left:-4em;width:.7em;height:.7em;border-radius:.35em}[class^='cd-iphone-6'].cd-gold .cd-camera::after,[class^='cd-iphone-6'].cd-rosegold .cd-camera::after,[class^='cd-iphone-6'].cd-silver .cd-camera::after{content:"";background:#3c3d3d;top:-1.4em;right:-3.5em;width:.5em;height:.5em;border-radius:.25em}[class^='cd-iphone-6'] .cd-ear{background:#292728;top:2.5em;left:50%;margin-left:-2em;width:4em;height:.5em;border-radius:.3em}[class^='cd-iphone-6'] .cd-screen{background:black;top:5em;left:50%;margin-left:-10.5em;width:21em;height:37em;border:solid .2em black;border-radius:.1em}[class^='cd-iphone-6'] .cd-home{bottom:.9em;left:50%;margin-left:-1.75em;width:3.5em;height:3.5em;border-radius:1.75em;border:.2em solid black}[class^='cd-iphone-6'] .cd-sound{top:5em;left:-.5em;height:1.8em;width:.2em}[class^='cd-iphone-6'] .cd-sound::before{content:"";top:4.4em;height:3.4em;width:.2em}[class^='cd-iphone-6'] .cd-sound::after{content:"";top:8.8em;height:3.4em;width:.2em}[class^='cd-iphone-6'] .cd-sleep{top:9.3em;right:-.5em;height:3.4em;width:.2em}.cd-iphone-6-plus .cd-body .cd-screen{top:5em;margin-left:-11.8em;width:23.6em;height:42em;border:solid .2em black;border-radius:.1em}.cd-blueprint .cd-sound{left:-2px !important}.cd-blueprint[class^='cd-iphone-6'] .cd-sleep{background:#000 !important;width:1px !important;border:none !important;right:-2px}.cd-blueprint[class^='cd-iphone-5'] .cd-sleep{background:#000 !important;height:1px !important;border:none !important;top:-2px}.cd-ipad,.cd-ipad>.cd-body{width:33.6em;height:48em}.cd-ipad .cd-body{background:#1e1e1e;border-radius:1.6em;border:0.25em solid #656565}.cd-ipad .cd-camera{background:#3c3d3d;width:.5em;height:.5em;top:1.6em;left:50%;margin-left:-.25em;border-radius:.3em}.cd-ipad .cd-screen{width:30em;height:40em;background:black;top:3.7em;left:50%;margin-left:-15em;border:.2em solid black}.cd-ipad .cd-home{width:2.2em;height:2.2em;border:0.2em solid #2c2b2c;bottom:.8em;border-radius:1.1em;left:50%;margin-left:-1em}.cd-ipad.cd-gold .cd-body{background:#fafafa;border-color:#ecdcc8}.cd-ipad.cd-gold .cd-home{border-color:#ecdcc8}.cd-ipad.cd-silver .cd-body{background:#fafafa;border-color:#bdbfbe}.cd-ipad.cd-silver .cd-home{border-color:#bdbfbe}.cd-mac{width:60em;height:34.8em}.cd-mac .cd-top{width:52em;height:34em;left:50%;margin-left:-26em;background:#d6d5da;border-radius:1.5em 1.5em .6em .6em}.cd-mac .cd-camera{width:.4em;height:.4em;background:#3c3d3d;left:50%;margin-left:-.2em;top:.8em;border-radius:.2em}.cd-mac .cd-screen{width:48em;height:30em;background:#3c3d3d;overflow:hidden;border:1px solid #3c3d3d;top:2em;left:50%;margin-left:-24em}.cd-mac .cd-bottom{width:100%;height:1em;bottom:0;background:#BDBDBD;border-radius:10em/1.2em;border-top-left-radius:0;border-top-right-radius:0}.cd-mac .cd-notch{width:10em;height:.5em;background:#d6d5da;left:50%;margin-left:-5em;bottom:.5em;border-radius:0 0 1em 1em;border-top:1px solid #BDBDBD}.cd-mac.cd-pro .cd-top{background:#1C1C1C}.cd-mac.cd-pro .cd-bottom{border-bottom-left-radius:2em;border-bottom-right-radius:2em}.cd-watch{width:16.6em;height:28em}.cd-watch .cd-bracket{height:19.4em;width:11.2em;left:50%;margin-left:-5.6em;border:0.5em solid #D8D8D8;border-radius:.8em;top:4.3em}.cd-watch [class$='-band']{width:9em;height:7.05em;background:#81DAF5;left:50%;margin-left:-4.5em}.cd-watch .cd-top-band{border-radius:.5em .5em 0 0;top:-1.2em;transform:perspective(30em) rotateX(45deg);-webkit-transform:perspective(30em) rotateX(45deg);-ms-transform:perspective(30em) rotateX(45deg)}.cd-watch .cd-bottom-band{border-radius:0 0 .5em .5em;bottom:-1.2em;transform:perspective(30em) rotateX(-45deg);-webkit-transform:perspective(30em) rotateX(-45deg);-ms-transform:perspective(30em) rotateX(-45deg)}.cd-watch .cd-crown{width:.8em;height:3.4em;right:0;top:50%;margin-top:-5em;background:#D8D8D8;border-radius:0 .4em .4em 0}.cd-watch .cd-button{width:.4em;height:5em;right:.4em;top:50%;background:#D8D8D8;border-radius:0 .3em .3em 0}.cd-watch .cd-body{height:18em;width:15.2em;top:50%;left:50%;margin-top:-9em;margin-left:-7.6em;border:0.7em solid #D8D8D8;border-radius:3em;background:black}.cd-watch .cd-screen{background:black;overflow:hidden;width:11.2em;height:14em;top:50%;left:50%;margin-top:-7em;margin-left:-5.6em;border-radius:.5em}.cd-watch .cd-screen>*{border-radius:.5em}.cd-watch.cd-no-bracket .cd-bracket{display:none}.cd-watch.cd-black .cd-crown,.cd-watch.cd-black .cd-button{background:#585858}.cd-watch.cd-black .cd-bracket,.cd-watch.cd-black .cd-body{border-color:#585858}.cd-watch.cd-gold .cd-crown,.cd-watch.cd-gold .cd-button{background:#e9d296}.cd-watch.cd-gold .cd-bracket,.cd-watch.cd-gold .cd-body{border-color:#e9d296}.cd-watch.cd-rosegold .cd-crown,.cd-watch.cd-rosegold .cd-button{background:#e9bfa9}.cd-watch.cd-rosegold .cd-bracket,.cd-watch.cd-rosegold .cd-body{border-color:#e9bfa9}.cd-watch.cd-white-band [class$='-band']{background:#F2F2F2}.cd-watch.cd-blue-band [class$='-band']{background:#81DAF5}.cd-watch.cd-green-band [class$='-band']{background:#C8FE2E}.cd-watch.cd-pink-band [class$='-band']{background:#F66E64}.cd-watch.cd-black-band [class$='-band']{background:#2E2E2E}.cd-watch.cd-brown-band [class$='-band']{background:#876450}.cd-watch.cd-tan-band [class$='-band']{background:#BEB0A6}.cd-watch.cd-navy-band [class$='-band']{background:#56586b}.cd-watch.cd-red-band [class$='-band']{background:#ff4838}.cd-watch.cd-linked-band [class$='-band']{background:linear-gradient(to bottom, #595959, #595959 10%, #B6B6B6 10%, #B6B6B6);background-size:100% 2em}.cd-watch.cd-blueprint .cd-bracket{top:4.6em;height:18.9em}.cd-watch.cd-blueprint .cd-crown,.cd-watch.cd-blueprint .cd-button{border-left:none !important}.cd-watch.cd-blueprint .cd-screen{border:none !important}body{font-size:14px}[class^='cd-'],[class^='cd-']::after,[class^='cd-']::before{margin:0;padding:0;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:relative}.cd-iphone-5s,.cd-iphone-5c,.cd-iphone-6,.cd-iphone-6-plus,.cd-ipad,.cd-mac{border:0;position:relative;z-index:50;font-size:14px;display:block}[class^='cd-'] *,[class^='cd-'] *::after,[class^='cd-'] *::before{position:absolute}.cd-screen{overflow:hidden}.cd-screen>*{display:none;position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;color:white;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;-webkit-user-drag:none;-moz-user-drag:none;user-drag:none}.cd-screen>*:first-child{display:block}.cd-screen.cd-screen-scrolling{overflow-y:scroll}.cd-screen.cd-screen-scrolling>*{bottom:none;height:auto}.cd-scale-10{font-size:10% !important}.cd-scale-20{font-size:20% !important}.cd-scale-30{font-size:30% !important}.cd-scale-40{font-size:40% !important}.cd-scale-50{font-size:50% !important}.cd-scale-60{font-size:60% !important}.cd-scale-70{font-size:70% !important}.cd-scale-80{font-size:80% !important}.cd-scale-90{font-size:90% !important}.cd-padded-device{margin:20px}.cd-fill-parent,.cd-device-loader{visibility:hidden}.cd-center{position:relative;margin-left:auto;margin-right:auto;display:block}[class^='cd-'].cd-blueprint *,[class^='cd-'].cd-blueprint *::after,[class^='cd-'].cd-blueprint *::before{background:white !important;border:1px solid #000 !important}.cd-blueprint .cd-sound,.cd-blueprint .cd-sound::before,.cd-blueprint .cd-sound::after{background:#000 !important;width:1px !important;border:none !important}.cd-blueprint .cd-screen *{border:none !important}.cd-slideshow>*:nth-child(n+2){display:none}.cd-smart-loader>:first-child{display:none}.cd-landscape-left.cd-iphone-5s,.cd-landscape-left.cd-iphone-5c,.cd-landscape-right.cd-iphone-5s,.cd-landscape-right.cd-iphone-5c{height:19.8em;width:42em}.cd-landscape-left.cd-iphone-6,.cd-landscape-right.cd-iphone-6{height:23em;width:48em}.cd-landscape-left.cd-iphone-6-plus,.cd-landscape-right.cd-iphone-6-plus{height:25.5em;width:53em}.cd-landscape-left.cd-ipad,.cd-landscape-right.cd-ipad{height:33.6em;width:48em}.cd-landscape-left>.cd-body,.cd-landscape-left>.cd-body>.cd-screen{transform-origin:0 0;-webkit-transform-origin:0 0;-ms-transform-origin:0 0}.cd-landscape-left>.cd-body{transform:rotate(-90deg) translate(-100%, 0);-webkit-transform:rotate(-90deg) translate(-100%, 0);-ms-transform:rotate(-90deg) translate(-100%, 0)}.cd-landscape-left>.cd-body>.cd-screen{transform:rotate(90deg) translate(0, -100%);-webkit-transform:rotate(90deg) translate(0, -100%);-ms-transform:rotate(90deg) translate(0, -100%)}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-5s>.cd-body>.cd-screen,.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-5c>.cd-body>.cd-screen{height:17.8em;width:31em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-6>.cd-body>.cd-screen{height:21em;width:37em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-iphone-6-plus>.cd-body>.cd-screen{height:23.6em;width:42em}.cd-landscape-left:not(.cd-landscape-fixed-screen).cd-ipad>.cd-body>.cd-screen{height:30em;width:40em}.cd-landscape-right>.cd-body,.cd-landscape-right>.cd-body>.cd-screen{transform-origin:0 0;-webkit-transform-origin:0 0;-ms-transform-origin:0 0}.cd-landscape-right>.cd-body{transform:rotate(90deg) translate(0, -100%);-webkit-transform:rotate(90deg) translate(0, -100%);-ms-transform:rotate(90deg) translate(0, -100%)}.cd-landscape-right>.cd-body>.cd-screen{transform:rotate(-90deg) translate(-100%, 0);-webkit-transform:rotate(-90deg) translate(-100%, 0);-ms-transform:rotate(-90deg) translate(-100%, 0)}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-5s>.cd-body>.cd-screen,.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-5c>.cd-body>.cd-screen{height:17.8em;width:31em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-6>.cd-body>.cd-screen{height:21em;width:37em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-iphone-6-plus>.cd-body>.cd-screen{height:23.6em;width:42em}.cd-landscape-right:not(.cd-landscape-fixed-screen).cd-ipad>.cd-body>.cd-screen{height:30em;width:40em}.cd-landscape-fixed-screen>.cd-body>.cd-screen{transform:none;-webkit-transform:none;-ms-transform:none;transform-origin:none;-webkit-transform-origin:none;-ms-transform-origin:none} diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..6de9e49 --- /dev/null +++ b/css/main.css @@ -0,0 +1,87 @@ +html, body { font-family: 'Inter', sans-serif; font-size: 16px; } + +hr { background-color: #e2e8f0; opacity: 1; } + +.h3, h3 { + padding-top:20px; +} + +header { background-color: #f7f7f7; } + +.bg-primary-docs { background-color: #2b58c9 !important; } + +@media (min-width: 768px) { .nav-docs { position: sticky; top: 0; z-index: 100; } + .sticky-sidebar { position: sticky; top: 100px; z-index: 99; } } +.nav-docs { background-color: white !important; border-bottom: 1px solid #e2e8f0; } + +.bg-light { background-color: #F1F5F8 !important; } + +.rounded-lg { border-radius: 1rem; } + +aside small { text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px; color: #2b58c9; margin-bottom: 0.25rem; display: block; } +aside ul { padding-left: 0; } +aside ul li { list-style: none; font-size: 0.875rem; margin: 0.2rem 0; } +aside ul li:hover, aside ul li.active { background-color: #2b58c921; border-radius: 4px; } +aside ul li:hover a, aside ul li.active a { color: #2b58c9 !important; } +aside ul li a { color: #616161; padding: 0.3rem 0.5rem; display: block; text-decoration: none; } + +.on-this-page { padding-left: 0; } +.on-this-page li { list-style: none; font-size: 0.875rem; margin: 0; } +.on-this-page li:hover, .on-this-page li.active { background-color: #2b58c921; border-radius: 4px; } +.on-this-page li:hover a, .on-this-page li.active a { color: #2b58c9 !important; } +.on-this-page li a { color: #616161; padding: 0.3rem 0.5rem; display: block; text-decoration: none; } + +.card-docs { border-color: transparent; } + +.device-container { background: white; } + +.doc-section { border: 5px solid #f7f7f9; border-radius: 5px; margin-bottom: 40px; } + +.twitter-share-button { -webkit-transform: scale(1.1); -moz-transform: scale(1.1); -ms-transform: scale(1.1); transform: scale(1.1); } + +.doc-top { padding: 10px; } + +.doc-title { font-weight: 600 !important; margin-bottom: 0.6rem; } + +small { font-size: 0.75rem; } + +pre { padding: 1rem; } + +.social-buttons { display: table; margin: 10px auto 20px auto; position: relative; } + +footer { padding: 20px 0; } +footer .text-muted { color: #c6cdd4 !important; } + +.compatibility-badge { color: #333333; border: 1px solid #dae4e9; background-color: #fafcfc; padding: 0.25rem 0.5rem 0.25rem 0.25rem; border-radius: 9999px; font-weight: 500; text-decoration: none; font-size: 14px; } +.compatibility-badge .badge-check { background-color: #51d88a; border-radius: 9999px; margin-right: 0.25rem; width: 16px; height: 16px; } +.compatibility-badge .badge-check img { color: white; } + +@media (max-width: 768px) { .docs-cta { margin-top: 16px !important; padding: 0.5rem; } } +.highlight { background-color: #f1f5f8; border-radius: 10px; } + +.supported-client-list { padding-left: 0.5rem; } +.supported-client-list li { list-style: none; font-size: 18px; } +.supported-client-list li img { width: 30px; height: 30px; margin-right: 0.5rem; } + +.email-click-preview span, .mobile-email-click-preview span { opacity: .75; } +.email-click-preview span[data-show]:hover, .email-click-preview span.active, .mobile-email-click-preview span[data-show]:hover, .mobile-email-click-preview span.active { opacity: 1; cursor: pointer; color: #2b58c9; } +.email-click-preview span.active, .mobile-email-click-preview span.active { font-weight: 600; } + +.btn-purple { background-color: #2b58c9; color: white; } +.btn-purple:hover { color: #2b58c9 !important; border-color: #2b58c9 !important; background-color: white !important; } + +.table-utilities { max-height: 293px; overflow-y: auto; margin-bottom: 1.5rem; } +.table-utilities th { font-weight: 500; } +.table-utilities th, .table-utilities td { border-bottom-color: #e2e8f0 !important; font-size: 13px; } +.table-utilities tbody .class { color: #2b58c9; font-weight: 500; font-family: var(--bs-font-monospace); white-space: nowrap; } +.table-utilities tbody .css { color: #6c757d; font-family: var(--bs-font-monospace); } +.table-utilities tbody .result { color: #6c757d; } +.table-utilities tbody tr:last-child td { border-bottom: 0 !important; } + +.badge-input, .badge-output { display: table; margin-bottom: -26px; position: relative; z-index: 10; } + +.badge-input { background-color: #6c757d; } + +.badge-output { background-color: #2b58c9; } + +a.anchor { display: block; position: relative; top: -75px; visibility: hidden; } diff --git a/css/prism.css b/css/prism.css new file mode 100644 index 0000000..bb7218c --- /dev/null +++ b/css/prism.css @@ -0,0 +1,139 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + diff --git a/css/syntax.css b/css/syntax.css new file mode 100644 index 0000000..daf76ad --- /dev/null +++ b/css/syntax.css @@ -0,0 +1,209 @@ +.highlight table td { padding: 5px; } +.highlight table pre { margin: 0; } +.highlight .cm { + color: #999988; + font-style: italic; +} +.highlight .cp { + color: #999999; + font-weight: bold; +} +.highlight .c1 { + color: #999988; + font-style: italic; +} +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; +} +.highlight .c, .highlight .cd { + color: #999988; + font-style: italic; +} +.highlight .err { + color: #a61717; + background-color: #e3d2d2; +} +.highlight .gd { + color: #000000; + background-color: #ffdddd; +} +.highlight .ge { + color: #000000; + font-style: italic; +} +.highlight .gr { + color: #aa0000; +} +.highlight .gh { + color: #999999; +} +.highlight .gi { + color: #000000; + background-color: #ddffdd; +} +.highlight .go { + color: #888888; +} +.highlight .gp { + color: #555555; +} +.highlight .gs { + font-weight: bold; +} +.highlight .gu { + color: #aaaaaa; +} +.highlight .gt { + color: #aa0000; +} +.highlight .kc { + color: #000000; + font-weight: bold; +} +.highlight .kd { + color: #000000; + font-weight: bold; +} +.highlight .kn { + color: #000000; + font-weight: bold; +} +.highlight .kp { + color: #000000; + font-weight: bold; +} +.highlight .kr { + color: #000000; + font-weight: bold; +} +.highlight .kt { + color: #445588; + font-weight: bold; +} +.highlight .k, .highlight .kv { + color: #000000; + font-weight: bold; +} +.highlight .mf { + color: #009999; +} +.highlight .mh { + color: #009999; +} +.highlight .il { + color: #009999; +} +.highlight .mi { + color: #009999; +} +.highlight .mo { + color: #009999; +} +.highlight .m, .highlight .mb, .highlight .mx { + color: #009999; +} +.highlight .sb { + color: #d14; +} +.highlight .sc { + color: #d14; +} +.highlight .sd { + color: #d14; +} +.highlight .s2 { + color: #d14; +} +.highlight .se { + color: #d14; +} +.highlight .sh { + color: #d14; +} +.highlight .si { + color: #d14; +} +.highlight .sx { + color: #d14; +} +.highlight .sr { + color: #009926; +} +.highlight .s1 { + color: #d14; +} +.highlight .ss { + color: #990073; +} +.highlight .s { + color: #d14; +} +.highlight .na { + color: #008080; +} +.highlight .bp { + color: #999999; +} +.highlight .nb { + color: #0086B3; +} +.highlight .nc { + color: #445588; + font-weight: bold; +} +.highlight .no { + color: #008080; +} +.highlight .nd { + color: #3c5d5d; + font-weight: bold; +} +.highlight .ni { + color: #800080; +} +.highlight .ne { + color: #990000; + font-weight: bold; +} +.highlight .nf { + color: #990000; + font-weight: bold; +} +.highlight .nl { + color: #990000; + font-weight: bold; +} +.highlight .nn { + color: #555555; +} +.highlight .nt { + color: #000080; +} +.highlight .vc { + color: #008080; +} +.highlight .vg { + color: #008080; +} +.highlight .vi { + color: #008080; +} +.highlight .nv { + color: #008080; +} +.highlight .ow { + color: #000000; + font-weight: bold; +} +.highlight .o { + color: #000000; + font-weight: bold; +} +.highlight .w { + color: #bbbbbb; +} +.highlight { + background-color: #f8f8f8; +} diff --git a/docs/example.md b/docs/example.md new file mode 100644 index 0000000..1e5ba23 --- /dev/null +++ b/docs/example.md @@ -0,0 +1,2564 @@ +--- +layout: docs +title: "Examples Page" +tagline: "Examples for how to show site content." +sections: + - Class Reference + - Usage + - Compiled Example +--- + +# Alerts + + + +
+ + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + +
ClassResult
.alertthe base alert class to setup structure
.alert-{{ item.name }}{{ item.name }} theme color
+
+ +{% include header.html name="Usage" hr="false" %} +```html +
+ This is a primary alert—check it out! +
+
+ This is a secondary alert—check it out! +
+
+ This is a success alert—check it out! +
+
+ This is a danger alert—check it out! +
+
+ This is a warning alert—check it out! +
+
+ This is a info alert—check it out! +
+
+ This is a light alert—check it out! +
+
+ This is a dark alert—check it out! +
+``` + +
+ This is a primary alert—check it out! +
+
+ This is a secondary alert—check it out! +
+
+ This is a success alert—check it out! +
+
+ This is a danger alert—check it out! +
+
+ This is a warning alert—check it out! +
+
+ This is a info alert—check it out! +
+
+ This is a light alert—check it out! +
+
+ This is a dark alert—check it out! +
+ +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
Well done! You successfully read this important alert message.
+``` + +Output +```html + + + + + + + +``` + + +# Badges + + +
+ + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + +
ClassResult
.badgethe base badge class to setup structure
.badge-{{ item.name }}{{ item.name }} theme color
+
+ + +{% include header.html name="Usage" hr="false" %} +Use the [Background Color](/docs/background-color), [Text Color](/docs/text-color), and [Border Radius](/docs/border-radius) classes to style badges. + +```html +Primary +Secondary +Success +Danger +Warning +Info +Light +Dark +``` + +Primary +Secondary +Success +Danger +Warning +Info +Light +Dark + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Badge +``` + +Output +```html + + + + + + + +``` + +# Align-X + + +
+ + + + + + + + + + + + +
ClassResult
.ax-leftalign horizontally left
.ax-rightalign horizontally right
.ax-centeralign horizontally center
+
+ +{% include header.html name="Usage" hr="false" %} +Align uses the `align="left|center|right` property on `table` to align anything horizontally. When it is used with a `table` or `td` it will put that property directly on them, however if it is used on any other tag it will wrap that element with a `table` and align it accordingly. +```html +
Align left on all viewport sizes

+
Align right on all viewport sizes

+
Align center on all viewport sizes
+Right Aligned Button +``` + +
Align left on all viewport sizes

+
Align right on all viewport sizes

+
Align center on all viewport sizes
+Right Aligned Button + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
Hello
+
WOWWWWW
+
Goodbye
+``` + +Output +```html + + + + + + + + + + + + + + + + + + + + + + + +``` + +# Align-Y + + +
+ + + + + + + + + + + + + + + +
ClassResult
.ay-topvertical-align: top;
.ay-middlevertical-align: middle;
.ay-bottomvertical-align: bottom;
.ay-baselinevertical-align: baseline;
.ay-text-topvertical-align: text-top;
.ay-text-bottomvertical-align: text-bottom;
+
+ +{% include header.html name="Usage" hr="false" %} +Vertical align allows you to vertically align the contents of a table cell. It can only be use in conjunction with a table cell. However most parts of Bootstrap Email are converted into tables so it can feel pretty automatic. You can also apply them directly to the parent `table` to adjust all of the `td` vertical alignments. If you are looking to vertically align child elements of a Stack, check out the [alignment section](/docs/stack#alignment) of the Stack docs. +```html + + + + + + + + + + + +
baselinetopmiddlebottomtext-toptext-bottom
+``` + + + + + + + + + + + + +
baselinetopmiddlebottomtext-toptext-bottom
+ + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html + + + + + + + + + + + +
baselinetopmiddlebottomtext-toptext-bottom
+``` + +Output +```html + + + + + + + + + + + +
baselinetopmiddlebottomtext-toptext-bottom
+``` + +# Background Color + + +
+ + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + {% for item in site.data.palette_colors %} + + {% endfor %} + +
ClassProperties
.bg-{{ item.name }}background-color: {{ item.color }};
.bg-{{ item.name }}background-color: {{ item.color }};
+
+ + +{% include header.html name="Usage" hr="false" %} +Since emails render mostly consistently with tables, many of the components are built into tables. These color classes apply to the current element as well as the child `` so you can use it on a component it will still work if the component is compiled into a table. It also uses the `bgcolor` property for support for old email clients. If a background color utility class is used on a `
` it will automatically be converted to a table with 100% width since divs don't have good background color support in many email clients. +```html +
.bg-primary
+
.bg-secondary
+
.bg-success
+
.bg-red-500
+
.bg-yellow-500
+``` + +
.bg-primary
+
.bg-secondary
+
.bg-success
+
.bg-red-500
+
.bg-yellow-500
+ +{% include header.html name="Customize" hr="true" %} +See the [Customize](/docs/customize) docs to learn more about customizing the color palette. + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
Blue 100
+
Blue 200
+
Blue 300
+
Blue 400
+
Blue 500
+
Blue 600
+
Blue 700
+
Blue 800
+
Blue 900
+``` + +Output +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +# Block + + +
+ + + + + + + + + + +
ClassResult
<block>, .to-tableturn the current element into a table and move the classes onto the table.
+
+ +{% include header.html name="Usage" hr="false" %} +Since many email clients have poor support for block elements such as `div`s, `table`s are usually used in their place. Much of Bootstrap Email automatically builds things into tables automatically and applies the correct styles. However there are certain time when that is not the case and you want to easy make something into a table without having to write out the full table structure. + +This is where `block` or `.to-table` come in. `block` is an HTML element and `to-table` is a class but both have the same result of turning the current element into a table. +```html + +
Hello
+Hello +``` + +Tip: Use the `w-full` class to make a table `width: 100%` to make it work like a div or block element. + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
Hello
+Hello +``` + +Output +```html + + + + + + + + + + + + + + +``` + +# Border Color + + +
+ + + + + + + + + + {% for color in site.data.theme_colors %} + + {% endfor %} + {% for color in site.data.palette_colors %} + + {% endfor %} + +
ClassProperties
.border-{{ color.name }}border-color: {{ color.color }};
.border-{{ color.name }}border-color: {{ color.color }};
+
+ +
The documentation on this page is no complete yet, work in progress 👍
+ + +# Border + + +
+ + + + + + + + + {% for border in site.data.border_widths %} + + + + + + {% endfor %} + +
ClassProperties
.border{{ border.name }}border: {{ border.width }}px solid #dee2e6;
.border-top{{ border.name }}border-top: {{ border.width }}px solid #dee2e6;
.border-right{{ border.name }}border-right: {{ border.width }}px solid #dee2e6;
.border-bottom{{ border.name }}border-bottom: {{ border.width }}px solid #dee2e6;
.border-left{{ border.name }}border-left: {{ border.width }}px solid #dee2e6;
+
+ +
The documentation on this page is no complete yet, work in progress 👍
+ +# Border Radius + + +
+ + + + + + + + + {% for border in site.data.border_radiuses %} + + + + + + {% endfor %} + +
ClassProperties
.rounded{{ border.name }}border-radius: {{ border.radius }}px;
.rounded-top{{ border.name }}border-top-left-radius: {{ border.radius }}px;
border-top-right-radius: {{ border.radius }}px;
.rounded-right{{ border.name }}border-top-right-radius: {{ border.radius }}px;
border-bottom-right-radius: {{ border.radius }}px;
.rounded-bottom{{ border.name }}border-bottom-left-radius: {{ border.radius }}px;
border-bottom-right-radius: {{ border.radius }}px;
.rounded-left{{ border.name }}border-top-left-radius: {{ border.radius }}px;
border-bottom-left-radius: {{ border.radius }}px;
+
+ +
The documentation on this page is no complete yet, work in progress 👍
+ +# Button + + +
+ + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + {% for item in site.data.palette_colors %} + {% if item.name != 'transparent' %} + + {% endif %} + {% endfor %} + {% for item in site.data.theme_colors %} + + {% endfor %} + {% for item in site.data.palette_colors %} + {% if item.name != 'transparent' %} + + {% endif %} + {% endfor %} + +
ClassResult
.btnthe base button class to setup structure
.btn-{{ item.name }}background-color: {{ item.color }};
.btn-{{ item.name }}background-color: {{ item.color }};
.btn-outline-{{ item.name }}border-color: {{ item.color }}; text-color: {{ item.color }}; background-color: transparent;
.btn-outline-{{ item.name }}border-color: {{ item.color }}; text-color: {{ item.color }}; background-color: transparent;
+
+ +{% include header.html name="Usage" hr="false" %} +Buttons are **ONLY** to be used with an anchor `` tag. The there are classes for all the theme and palette colors, so you can use `btn-primary` as well as `btn-blue-300`. + +```html +Primary +Secondary +Success +Danger +Warning +Info +Light +Dark +``` + +Primary +Secondary +Success +Danger +Warning +Info +Light +Dark + +
+ Warning: You must supply a url in the href. Using just pound sign is not enough for some emails to render an anchor tag correctly. +
+ +{% include header.html name="Usage" hr="false" %} +You can use *outlined* versions of every button by simply adding the `outline` keyword to the class like `btn-outline-primary` or `btn-outline-green-500`. + +```html +Primary +Green +``` +Primary +Green + +{% include header.html name="Sizes" hr="true" %} +You can use `btn-sm` or `btn-lg` for smaller or larger buttons and text respectively. If you want to just adjust the padding size of a button you can use the [Padding](/docs/padding) utility classes. +```html +Large button +Large button +``` + +Large button +Large button + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Click Me +``` + +Output +```html + + + + + + + +``` + +# Card + + +
+ + + + + + + + + + + +
ClassResult
.cardbackground-color: white; border: 1px solid #e2e8f0; border-radius: 6px;
.card-bodypadding: 20px;
+
+ + +{% include header.html name="Usage" hr="false" %} +Use to wrap content in a solid gray bordered container with rounded corners. Works well sitting on top of an email body with a `.bg-light` class and inside of a `.container`. + +By default `.card` has no padding, you can use a card with or without a `.card-body`. Just like in Bootstrap a `.card-body` is just used to give `20px` padding to the card, you can also use a [padding](/docs/padding) utility to customize padding. + +```html +
+
+ This is some text within a card body. +
+
+``` + +
+
+ This is some text within a card body. +
+
+ + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
+
+ Some quick example text to build on the card title and make up the bulk of the card's content. +
+
+ +``` + +Output +```html + + + + + + + +``` + +# Configure + +{% include header.html name="Config" hr="false" %} +Configure different aspects of Bootstrap Email. It will look for a file in the working directory for `bootstrap_email.config.rb`. If you are using Rails, put this file in `config/initializers/bootstrap_email.rb`. Here is a list of the config options that you can customize. +```ruby +BootstrapEmail.configure do |config| + # Defaults to ./bootstrap-email.scss or ./app/assets/stylesheets/bootstrap-email.scss in rails + config.sass_email_location = File.expand_path('path/to/bootstrap-email.scss', __dir__) + # Defaults to ./bootstrap-head.scss or ./app/assets/stylesheets/bootstrap-head.scss in rails + config.sass_head_location = File.expand_path('path/to/bootstrap-head.scss', __dir__) + # Custom sass that can be passed in to customize sass variables and such for the email sass + config.sass_email_string = '// Pass in custom sass' + # Custom sass that can be passed in to customize sass variables and such for the head sass + config.sass_head_string = '// Pass in head custom sass' + # Array of folders to add to the SASS load path to support imports in the custom SASS files + config.sass_load_paths = [File.expand_path('some/folder', __dir__)] + # Defaults to ./.sass-cache or ./tmp/cache/bootstrap-email/.sass-cache in rails + config.sass_cache_location = [File.expand_path('some/folder', __dir__)] + # Defaults to true, is disabled during CLI commands that output to standard out + config.sass_log_enabled = true + # Turn on or off whether or not rails will also include a plain text email part, Default: true + config.generate_rails_text_part +end +``` + +{% include header.html name="Config Path" hr="true" %} +If you want to provide a path to a config file, you can use the `options: {config_path: 'path'}}` option in the `BootstrapEmail::Compiler.new()` method or with the `-c` flag via the CLI. +- Using the CLI: +```bash +bootstrap-email -c path/to/bootstrap-email.config.rb +``` +- Using Ruby: +```ruby +html = '
Hello
' +path = File.expand_path('path/to/bootstrap-email.config.rb', __dir__) +BootstrapEmail::Compiler.new(html, options: {config_path: path}).perform_full_compile +``` +Also any config above, `sass_email_location` for example, can be passed in via the `options` hash. + +{% include header.html name="Customize Sass" hr="true" %} +If you want to customize styles like colors or spacing or even want to add custom styles to be compiled with the SCSS and inlined into your email you can use a config file. By default Bootstrap Email will look for a `bootstrap-email.scss` file in the root of the working directory / project. + +Here are the 3 files of SASS variables that you can customize by overriding: +1. [General Variables](https://github.com/bootstrap-email/bootstrap-email/blob/master/core/scss/_variables.scss) +2. [Color Variables](https://github.com/bootstrap-email/bootstrap-email/blob/master/core/scss/_colors.scss) +2. [Utility Variables](https://github.com/bootstrap-email/bootstrap-email/blob/master/core/scss/_utilities.scss) + +This is what a basic config file should contain: +```scss +//= @import bootstrap-email; +``` + + This allows you to customize all you want surrounding the base SCSS that gets pulled in. Here is an example of setting different colors to be used: +```scss +// Override all colors to black +$primary: #000000; +$secondary: #000000; +$success: #000000; +$info: #000000; +$warning: #000000; +$danger: #000000; +$light: #000000; +$dark: #000000; + +//= @import bootstrap-email; + +// Make all links pink +a { + color: pink; +} +``` +For more examples of variables that can be overridden check out the SCSS files for [colors](https://github.com/bootstrap-email/bootstrap-email/blob/v1-dev/core/scss/_colors.scss), [utilities](https://github.com/bootstrap-email/bootstrap-email/blob/v1-dev/core/scss/_utilities.scss), and [variables](https://github.com/bootstrap-email/bootstrap-email/blob/v1-dev/core/scss/_variables.scss) + +{% include header.html name="Additional Methods" hr="true" %} + +`BootstrapEmail.reset_config!` to reset all the configuration. + +`BootstrapEmail.clear_sass_cache!` to clear the cached sass files. + +# Container + + +
+ + + + + + + + + + + +
ClassResult
.containercenter aligned, max width 600px, left and right padding
.container-fluidfull width, left and right padding
+
+ +{% include header.html name="Usage" hr="false" %} +Using the `container` class is the most common default email structure recommended. It should be used to wrap your entire pages contents. It has a 600px max-width which is standard for broad email support. It will be responsive on mobile devices. + +```html +
+ +
+``` + +A `container-fluid` is unlike a container in that it doesn't have it's max-width set. It does however still have padding on the edges to give the content better spacing towards the edge of the email. + +```html +
+ +
+``` + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
+ +
+``` + +Output +```html + + + + + + + +``` + +# Display + + +
+ + + + + + + + + {% for display in site.data.displays %} + + {% endfor %} + +
ClassResult
.d-{{ display }}display: {{ display }};
+
+ +

Looking for flexbox support? Check out stack documentation for flexbox-like support in email using tables.

+ +{% include header.html name="Responsive" hr="false" %} +Here is an example of using the display utility to show and hide an element on mobile and hidden on desktop and vice versus. +```html +Hidden on Desktop +Hidden on Mobile +``` + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Hidden on Desktop +Hidden on Mobile +``` + +Output +```html + + +Hidden on Mobile +``` + +# Font Weight + + +
+ + + + + + + + + {% for weight in site.data.font_weights %} + + {% endfor %} + +
ClassProperties
.fw-{{ weight }}font-weight: {{ weight }};
+
+ +{% include header.html name="Usage" hr="false" %} +A simple way to adjust the weight of font. +```html +

Font weight 100

+

Font weight 200

+

Font weight 300

+

Font weight 400

+

Font weight 500

+

Font weight 600

+

Font weight 700

+

Font weight 800

+

Font weight 900

+``` +

Font weight 100

+

Font weight 200

+

Font weight 300

+

Font weight 400

+

Font weight 500

+

Font weight 600

+

Font weight 700

+

Font weight 800

+

Font weight 900

+ +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +

Font weight 100

+

Font weight 200

+

Font weight 300

+

Font weight 400

+

Font weight 500

+

Font weight 600

+

Font weight 700

+

Font weight 800

+

Font weight 900

+``` + +Output +```html +

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+

Some text here with some amount of weight

+``` + +# Gap + + +
+ + + + + + + + + {% for size in site.data.spacings %} + + {% endfor %} + {% for size in site.data.spacings %} + + {% endfor %} + {% for size in site.data.spacings %} + + {% endfor %} + +
ClassResult
.gap-{{ size }}{{ size | times: 4 }}px gap on all sides
.gap-x-{{ size }}{{ size | times: 4 }}px horizontal gap
.gap-y-{{ size }}{{ size | times: 4 }}px vertical gap
+
+ +{% include header.html name="Usage" hr="false" %} +Just like the margin and padding classes, multiply the class number by 4px to get the size. Ex. gap-10 is 40px. + +{% include header.html name="Grid" hr="true" %} +By default the grid `.row` class has `24px` horizontal gutters. You can apply these gap classes to the row class to change horizontal and vertical gutters `row gap-4` will add `16px` gutters on all sizes. Top and bottom gutters are useful for when a grid is responsive and stacks columns vertically on mobile. [More about using Gap with Grid](/docs/grid#gap) + +{% include header.html name="Stack" hr="true" %} +Use gap here to spread elements in a stack, horizontally if it is a `stack-x` and vertically if it is a `stack-y`. [More about using Gap with Stacks](/docs/grid#gap) + +# Grid + + +
+ + + + + + + + + + {% for item in site.data.grid_cols %} + + {% endfor %} + +
ClassResult
.rowparent element to build a grid
.col-{{ item.name }}width: {{ item.width }};
+
+ +{% include header.html name="Usage" hr="false" %} +Grids work just like they do in Bootstrap, based on a 12 column grid. Make a row and give it columns. By default the grid holds it's structure on every device. It has a default horizontal gutter between elements of `24px`. + +```html +
+
.col-3
+
.col-4
+
.col-5
+
+``` + +
+
.col-3
+
.col-4
+
.col-5
+
+ +{% include header.html name="Responsive" hr="true" %} +You can use the responsive lg modifier to make the grid snap back to vertical stacking on smaller devices. + +```html +
+
.col-3
+
.col-4
+
.col-5
+
+``` + +{% include header.html name="Gap" hr="true" %} +To customize horizontal and vertical gutters see the [Gap Docs](/docs/gap) for more details. For example `row gap-0` would remove the gap between cells. In the example below, the `gap-12` adds a `48px` gap between all cells. The default gap between cells is `24px`. + +```html +
+
.col-3
+
.col-4
+
.col-5
+
+``` + +
+
.col-3
+
.col-4
+
.col-5
+
+ +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
+
This is a 1/4 of the row
+
This is a 1/4 of the row
+
This is a 1/2 of the row
+
+``` + +Output +```html + + + + + + + + + +``` + +# Height + + +
+ + + + + + + + + + + {% for size in site.data.sizings %} + + {% endfor %} + + {% for size in site.data.sizings %} + + {% endfor %} + +
ClassProperties
.h-fullheight: 100%;
.h-autoheight: auto
.h-{{ size }}height: {{ size | times: 4 }}px;
.max-h-fullmax-height: 100%;
.max-h-{{ size }}max-height: {{ size | times: 4 }}px;
+
+ +{% include header.html name="Usage" hr="false" %} +Like the [padding utilities](/docs/padding) it uses `4px` as the increment size for each number. So `h-10` is `10 * 4px` which is `40px`. To do 100% height you can use `h-full`. These height and max-height utilities are really useful for images because **images need a width and/or a height** for them to be rendered properly in many versions of Outlook. +```html + /* image 48px width */ + /* image 600px width, use this for an image that is "full width" in a container in an email */ +``` + +In combination with the [width](/docs/width) utilities you can do things like make a circle with number in it. +```html +
1
+``` +
1
+ + +{% include header.html name="Responsive" hr="true" %} +By default these classes target all devices. However if you just wanted to target desktop you could do `.h-lg-4`. For all of these classes you can apply a `lg-` to the middle to make it just apply to desktop devices. Or say you want a 100% button on mobile and a 40px height centered button on desktop. That would look like this: +```html +Tada +``` + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
1
+``` + +Output +```html + + + + + + + +``` + +# HR + + +
+ + + + + + + + + + +
ClassResult
<hr>gray horizontal rule full width
+
+ +{% include header.html name="Usage" hr="false" %} +Add a horizontal rule to separate content. A consistent one that works and looks good in all email clients. By default it has top and bottom spacing of 20px, to use different top and bottom margin us a [margin](/docs/margin) utility class. + +```html +
+Hello +
+``` +
+Hello +
+ +{% include header.html name="Compiled Example" hr="false" %} +Input +```html +
+``` + +Output +```html + + + + + + + + + + + + + + + + + + + + + +``` + +# Image + + +
+ + + + + + + + + + +
ClassResult
.img-fluid + height: auto;
+ max-width: 100%;
+ width: 100%; +
+
+ +{% include header.html name="Usage" hr="false" %} +Images can be very complicated in emails. Luckily Bootstrap email makes it easy. Images need to have at least either a width or a height for them to render properly in something like Outlook. To keep an element full width of it's container, give it the `img-fluid`. Doing so will set properties like the `width="100%"` attribute on the img tag which is required for Outlook rendering. + +```html +Some Image +``` + +For doing something other than 100% image with, use the sizing width and height utility classes with your images: [Width Docs](/docs/width) & [Height Docs](/docs/height). + +{% include header.html name="Accessibility" hr="true" %} +Make sure you include a descriptive `alt` property on every image, not only does this help with accessibility but it's not uncommon for images to be blocked in an email client and this helps user see what image is being blocked. [More on alt tags](https://moz.com/learn/seo/alt-text) + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Some Image +``` + +Output +```html +Some Image +``` + +# Line Height + + +
+ + + + + + + + + {% for size in site.data.line_heights %} + + {% endfor %} + +
ClassProperties
.lh-{{ size.name }}line-height: {{ size.size }};
+
+ +{% include header.html name="Usage" hr="false" %} +A simple way to adjust the line height of text. +```html +

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+``` + +

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+ +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+``` + +Output +```html +

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+

This is test of text with different amounts of line height. What do you think of the line height for this text? Is it too large? Too small? The world may never know but I do hope that this works.

+``` + +# Margin + + +
+ + + + + + + + + {% for spacing in site.data.spacings %} + + + + {% endfor %} + {% for spacing in site.data.spacings %} + + {% endfor %} + +
ClassProperties
.my-{{ spacing }}Make a {{ spacing | times: 4 }}px spacer above and below
.mt-{{ spacing }}Make a {{ spacing | times: 4 }}px spacer above
.mb-{{ spacing }}Make a {{ spacing | times: 4 }}px spacer below
.s-{{ spacing }}Make a spacer {{ spacing | times: 4 }}px tall
+
+Note: Margin is only supported on the top and bottom. + +{% include header.html name="Margin Usage" hr="false" %} +There are two types of spacing Bootstrap Email supports. [Padding](/docs/padding) (applied to the inside of table cells) and [Margin](/docs/margin) (in the form of vertical spacers which are used to take up space between elements vertically). + +Margin is very inconsistently supported in email clients. Instead of using `margin` in css the margin classes create spacers above and/or below and element for simpler syntax like Bootstrap. This example adds a spacer of 12px above and below the middle card. See [space between](/docs/space-between) for more info on adding spacers between elements. +```html +
Top Card
+
Middle Card (with margin above and below)
+
Bottom Card
+``` + +
Top Card
+
Middle Card (with margin about and below)
+
Bottom Card
+ +{% include header.html name="Spacer Usage" hr="true" %} +```html +
+``` +Spacers hold not content, they are just put into the document to sit between elements in a vertical flow. + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Button with mega margin +``` + +Output +```html + + + + + + + + + + + + + + + + + + + + + +``` + +# Padding + + + +
+ + + + + + + + + {% for spacing in site.data.spacings %} + + + + + + + + {% endfor %} + +
ClassProperties
.p-{{ spacing }}padding: {{ spacing | times: 4 }}px;
.px-{{ spacing }}padding-left: {{ spacing | times: 4 }}px; padding-right: {{ spacing | times: 4 }}px;
.py-{{ spacing }}padding-top: {{ spacing | times: 4 }}px; padding-bottom: {{ spacing | times: 4 }}px;
.pt-{{ spacing }}padding-top: {{ spacing | times: 4 }}px;
.pr-{{ spacing }}padding-right: {{ spacing | times: 4 }}px;
.pb-{{ spacing }}padding-bottom: {{ spacing | times: 4 }}px;
.pl-{{ spacing }}padding-left: {{ spacing | times: 4 }}px;
+
+ +{% include header.html name="Usage" hr="false" %} +There are two types of spacing Bootstrap Email supports. Padding (applied to the inside of table cells) and [Margin](/docs/margin) (in the form of vertical spacers which are used to take up space between elements vertically). +```html +A Button with lots of padding +``` + +A Button with lots of padding + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Button with mega padding +``` + +Output +```html + + + + + + + +``` + +# Preview Text + + +
+ + + + + + + + + + +
TagResult
<preview>custom element for adding preview text to emails
+
+ +If you aren't familiar with preview text, it is the text that shows up in your inbox as a preview of the content inside the email. Here is more info about what it is and how it works on [Litmus.com](https://litmus.com/blog/the-ultimate-guide-to-preview-text-support) + + +{% include header.html name="Usage" hr="true" %} +Bootstrap Email has a custom `` tag you can use to define preview text in your emails. This way it will show up as a preview of your email in your inbox but will be completely hidden when viewing the email itself. + +```html +This text will show up as a preview but not in the email! +``` + +The message will also be padded with ` ` at the end of the message so that if you have a short preview text the contents of the email will not flow into the inbox preview. + +Note: This element will be moved to just under the `` tag when compiled. I would suggest keeping it in the top of your document for your sake but it can really be anywhere. + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Act quick before the offer ends! +``` + +Output +```html + +``` + +# Responsive + +This is a list of all the components that are responsive. + + + +When you see the + + + + + + Responsive + + badge it means the component has responsive breakpoints for different sized screens. The breakpoint is anything 600px or wider is considered `lg` and anything under 600px is `sm`. By default everything is "mobile first" which means the default class is both screen sizes. + +A class that uses the `-lg` infix is only large devices. For example `w-full` is `width: 100%` on all screen sizes. But `w-full w-lg-10` is `width: 100%` on small screens and `width: 40px` on large screen sizes. + +Note: Some email clients like Gmail mobile do not support any media queries and so some responsive functions do not work on them. + +Not everything that is responsive uses these names. For example the `.container` is responsive by default and expands and contracts on different screens. + +# Space Between + + +
+ + + + + + + + + {% for spacing in site.data.spacings %} + + {% endfor %} + +
ClassProperties
.space-y-{{ spacing }}Make a {{ spacing | times: 4 }}px spacer between every child element
+
+ +{% include header.html name="Usage" hr="false" %} +Similar to space between in [Tailwind](https://tailwindcss.com/docs/space), this allows you to space elements evenly vertically. Good for things like paragraphs of text. If you want horizontal spacing between elements or more complex alignment, check out the [Stack](/docs/stack) documentation. +```html + + +``` +
+ Space between these buttons +
+ +
+ Space between these buttons +
+ + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html + +``` + +Output +```html +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+``` + +# Stack + + +
+ + + + + + + + + + + + + + + + + + +
ClassResult
.stack-rowgenerate a horizontals stack
.stack-colgenerate a vertical stack
.stack-ax-lefttext-align: left;
.stack-ax-centertext-align: center;
.stack-ax-righttext-align: right;
.stack-ay-topvertical-align: top;
.stack-ay-middlevertical-align: middle;
.stack-ay-bottomvertical-align: bottom;
.stack-ay-baselinevertical-align: baseline;
+
+ +{% include header.html name="Usage" hr="false" %} +Stacks work similar to the way flexbox does to lay children elements out in a row or a column. To use a stack, all you need to do a wrap children elements in either a `stack-x` for row or a `stack-y` for a column. + +```html +
+
Lay a group of things
+ +
Out horizontally
+
+``` + +
+
Lay a group of things
+ +
Out horizontally
+
+ +{% include header.html name="Gap" hr="true" %} +Stacks are made really useful when combined with [Gaps](/docs/gap). Using a gap you can space a few icons out. + +```html +
+ + + +
+
+ + + +
+``` + +
+ + + +
+
+ + + +
+ +{% include header.html name="Alignment" hr="true" %} +Stacks use HTMl tables to generate their layouts, that means we can use the power of table cells to align their contents horizontally and vertically. The classes `stack-ax-left`, `stack-ax-center`, and `stack-ax-right` center contents horizontally (**Note: you must either you an inline or inline-block child element for this to work as expected**). The classes `stack-ay-top`, `stack-ay-middle`, `stack-ay-bottom`, and `stack-ay-baseline` are for vertical alignment of child elements. By default everything defaults to top left alignment. + +```html +
+ + + +
+
+ + + +
+
+ + + +
+``` +
+ + + +
+
+ + + +
+
+ + + +
+ + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
+
stack item 1
+
stack item 2
+
stack item 3
+
stack item 4
+
stack item 5
+
stack item 6
+
+``` + +Output +```html + + + + + + + + + + + + +``` + +# Table + + +
+ + + + + + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + +
ClassResult
.tableapplies the basic styles for a table, required for use with classes below
.table-borderedtable with border on all size
.table-stripedtable with every other row striped
.thead-lightlight table header
.thead-darkdark table header
.table-{{ item.name }}background-color: {{ item.color }};
+
+ +{% include header.html name="Usage" hr="false" %} +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Col 1Col 2Col 3Col 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
+``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Col 1Col 2Col 3Col 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
+ +{% include header.html name="Compiled Example" hr="true" %} + +Input +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Col 1Col 2Col 3Col 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
+``` + +Output +```html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Col 1Col 2Col 3Col 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
Col Data 1Col Data 2Col Data 3Col Data 4
+``` + +# Text Align + + +
+ + + + + + + + + + + + + +
ClassProperties
.text-lefttext-align: left;
.text-righttext-align: right;
.text-centertext-align: center;
.text-justifytext-align: justify;
+
+ +{% include header.html name="Usage" hr="false" %} +A simple way to adjust the line height of text. +```html +
This text is to the left
+
This text is to the center
+
This text is to the right
+
This text is justified which means it fills the whole line when it wraps so the start and end of the text with touch both the left and right side.
+``` + +
This text is to the left
+
This text is to the center
+
This text is to the right
+
This text is justified which means it fills the whole line when it wraps so the start and end of the text with touch both the left and right side.
+ +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +
This text is to the left
+
This text is to the center
+
This text is to the right
+
This text is justified which means it fills the whole line when it wraps so the start and end of the text with touch both the left and right side.
+``` + +Output +```html +
This text is to the left
+
This text is to the center
+
This text is to the right
+
This text is justified which means it fills the whole line when it wraps so the start and end of the text with touch both the left and right side.
+``` + +# Text Color + + +
+ + + + + + + + + + {% for item in site.data.theme_colors %} + + {% endfor %} + {% for item in site.data.palette_colors %} + + {% endfor %} + +
ClassProperties
.text-{{ item.name }}color: {{ item.color }};Email
.text-{{ item.name }}color: {{ item.color }};Email
+
+ +{% include header.html name="Usage" hr="false" %} +A simple way to adjust the line height of text. +```html +Primary Text color +Muted Text color +Danger Text color +Green 500 Text color +Yellow 500 Text color +``` + +Primary Text color +Muted Text color +Danger Text color +Green 500 Text color +Yellow 500 Text color + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Primary Text color +Muted Text color +Danger Text color +Green 500 Text color +Yellow 500 Text color +``` + +Output +```html +Primary Text color +Muted Text color +Danger Text color +Green 500 Text color +Yellow 500 Text color +``` + +# Text Size + + +
+ + + + + + + + + {% for size in site.data.font_sizes %} + + {% endfor %} + +
ClassProperties
.text-{{ size.name }}font-size: {{ size.size }}px;
+
+ +{% include header.html name="Usage" hr="false" %} +These utility classes are good for more fine tuned adjustments of font size. +```html +

Text size xs

+

Text size sm

+

Text size base

+

Text size lg

+

Text size xl

+

Text size 2xl

+

Text size 3xl

+

Text size 4xl

+

Text size 5xl

+

Text size 6xl

+

Text size 7xl

+``` +{% for size in site.data.font_sizes %} +

Text size {{ size.name }}

+{% endfor %} + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +Text size xs +Text size sm +Text size base +Text size lg +Text size xl +Text size 2xl +Text size 3xl +Text size 4xl +Text size 5xl +Text size 6xl +Text size 7xl +``` + +Output +```html +Text size xs +Text size sm +Text size base +Text size lg +Text size xl +Text size 2xl +Text size 3xl +Text size 4xl +Text size 5xl +Text size 6xl +Text size 7xl +``` + +# Themable + +When you see the + + + + + + Themeable + + badge it means the component has the following classes that you can use color the component. Just like Bootstrap, the convention is `{component}-{theme-color}`. + +```html +{component}-primary +{component}-secondary +{component}-success +{component}-danger +{component}-warning +{component}-info +{component}-light +{component}-dark +``` + +Bootstrap Email uses the same default color scheme that Bootstrap 4 does. +
primary
+
secondary
+
success
+
danger
+
warning
+
info
+
light
+
dark
+ + + +#### Customize Theme + +Bootstrap Email makes is really easy to customize the theme colors. You can override each color in your sass file before the `bootstrap-email` import: +```scss +$theme-primary: #007bff; +$theme-secondary: #868e96; +$theme-success: #28a745; +$theme-danger: #dc3545; +$theme-warning: #ffc107; +$theme-info: #17a2b8; +$theme-light: #f8f9fa; +$theme-dark: #343a40; + +@import 'bootstrap-email'; +``` + +Note: These are the current defaults for the theme colors. + +# Typography + + +
+ + + + + + + + + + + + + + + + + + + + +
ClassResult
<h1>, .h1font-size: 36px;
<h2>, .h2font-size: 32px;
<h3>, .h3font-size: 28px;
<h4>, .h4font-size: 24px;
<h5>, .h5font-size: 20px;
<h6>, .h6font-size: 16px;
<strong>font-weight: bold;
<em>font-style: italic;
.underline, <u>text-decoration: underline;
.no-underlinetext-decoration: none;
.line-through, <s>text-decoration: line-through;
+
+ +{% include header.html name="Usage" hr="false" %} +Many basic typography options. If you want more text control check out [Text Align](/docs/text-align), [Text Color](/docs/text-color), [Text Size](/docs/text-size), [Font Weight](/docs/font-weight), and [Line Height](/docs/line-height). +```html +

h1

+

h2

+

h3

+

h4

+
h5
+
h6
+
any tag as h1
+
any tag as h2
+
any tag as h3
+
any tag as h4
+
any tag as h5
+
any tag as h6
+

Display 1

+

Display 2

+

Display 3

+

Display 4

+

Display 5

+

Display 6

+bold text +underlined text +italicized text +strike through text +``` + +# h1 +## h2 +### h3 +#### h4 +##### h5 +###### h6 +# any tag as h1 +## any tag as h2 +### any tag as h3 +#### any tag as h4 +##### any tag as h5 +###### any tag as h6 +bold text +underlined text +italicized text +strike through text + +{% include header.html name="Compiled Example" hr="true" %} +Input +```html +

This is an h1

+

This is an h2

+

This is an h3

+

This is an h4

+
This is an h5
+
This is an h6
+Bold Text +Bold Text +Underlined Text +Italic Text +Strike Through Text +``` + +Output +```html +

This is an h1

+

This is an h2

+

This is an h3

+

This is an h4

+
This is an h5
+
This is an h6
+Bold Text +Bold Text +Underlined Text +Italic Text +Strike Through Text +``` + +# Usage + +#### Environment Support +- [Command Line](#command-line) +- [Ruby](#ruby) +- [Ruby on Rails](#ruby-on-rails) +- [Online editor](#online-editor) + +{% include header.html name="Command Line" hr="false" %} + +[Ruby](https://www.ruby-lang.org/en/documentation/installation/) is required to be installed on the machine you are using to run Bootstrap Email. + +1: Install bootstrap email via [Ruby Gems](https://rubygems.org/gems/bootstrap-email) +```bash +gem install bootstrap-email +``` + +2: There are many ways to use the command line to compile emails: +```bash +# compile all files ending in .html in the current directory +bootstrap-email +# compile the file email.html and save it to the file out.html +bootstrap-email email.html > out.html +# compile a relative path to a file +bootstrap-email ./public/index.html +# specify a path pattern and a destination directory for compiled emails to be saved to +bootstrap-email -p 'emails/*' -d 'emails/compiled/*' +# compile for a string +bootstrap-email -s 'Some Button' +# compile to plain text +bootstrap-email -t -s 'Some Button' +# pipe a file into bootstrap-email +cat input.html | bootstrap-email +# specify config path to use to customize things like colors +bootstrap-email -c bootstrap-email.scss +``` + +Help: run the command `bootstrap-email -h` for help on all options. + +{% include header.html name="Ruby" hr="false" %} + +1: Add Bootstrap Email to your `Gemfile` + +```ruby +gem 'bootstrap-email' +``` + +2: Usage is simple, there are two ways to call Bootstrap Email, string and file. +```ruby +# Pass in any html string or html document as a string +html = 'A button' +BootstrapEmail::Compiler.new(html).perform_full_compile + +# Pass in a full path to a file +file_path = File.expand_path('path/to/a/file.html', __dir__) +BootstrapEmail::Compiler.new(file_path, type: :file).perform_full_compile +``` + +{% include header.html name="Ruby on Rails" hr="false" %} + +1: Add Bootstrap Email to your `Gemfile` + +```ruby +gem 'bootstrap-email' +``` + +2: You need to create the mailer template which will wrap the email content. Create the file `/app/views/layouts/bootstrap-mailer.html.erb` and paste this HTML into it. (It is very similar to the default mailer). +```erb + + + + + + + + + + + <%= yield %> + + +``` + +3: Specify the layout for the actions you want to build with bootstrap email. You can specify it for all with ApplicationMailer. +```ruby +class ApplicationMailer < ActionMailer::Base + layout 'bootstrap-mailer' +end +``` +Refer to the [official ActionMailer documentation](http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-layouts) on more info about using a different layout for different mailers. + + + + +4: That's it! Now all you need to do to use it instead of using the `mail()` method, you use the `bootstrap_mail()` method to kick off Bootstrap Email compilation! + +```ruby +class ExampleMailer < ApplicationMailer + def greet + bootstrap_mail( + to: 'to@example.com', + from: 'from@example.com', + subject: 'Hi From Bootstrap Email', + ) + end +end +``` + +You can also use the `format` in the usual way. +```ruby +class ExampleMailer < ApplicationMailer + def greet + bootstrap_mail( + to: 'to@example.com', + from: 'from@example.com', + subject: 'Hi From Bootstrap Email', + ) do |format| + format.html { layout 'custom_bootstrap_layout' } + format.text # here example_mailer.text.erb is used + end + end +end +``` + +{% include header.html name="Online editor" hr="false" %} +An easier, but less integrated and customizable way to use Bootstrap Email is the online editor. +- Write an email and test it's rendering quickly +- Test on desktop and mobile responsive device widths +- Save emails to your account to come back to +- Send test emails directly to your inbox + +Try the Online Editor + + +# Width + + +
+ + + + + + + + + + + {% for size in site.data.sizings %} + + {% endfor %} + + {% for size in site.data.sizings %} + + {% endfor %} + +
ClassProperties
.w-fullwidth: 100%;
.w-autowidth: auto
.w-{{ size }}width: {{ size | times: 4 }}px;
.max-w-fullmax-width: 100%;
.max-w-{{ size }}max-width: {{ size | times: 4 }}px;
+
+ +{% include header.html name="Usage" hr="false" %} +Like the [padding utilities](/docs/padding) it uses `4px` as the increment size for each number. So `w-10` is `10 * 4px` which is `40px`. To do 100% width you can use `w-full`. These width and max-width utilities are really useful for images because **images need a width and/or a height** for them to be rendered properly in many versions of Outlook. +```html + /* image 48px width */ + /* image 600px width, use this for an image that is "full width" in a container in an email */ +``` + +{% include header.html name="Responsive" hr="true" %} +By default these classes target all devices. However if you just wanted to target desktop you could do `.w-lg-4`. For all of these classes you can apply a `lg-` to the middle to make it just apply to desktop devices. Or say you want a 100% button on mobile and a auto width centered button on desktop. That would look like this: +```html +Tada +``` + +
The documentation on this page is no complete yet, work in progress 👍
diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..cd9068e Binary files /dev/null and b/favicon.png differ diff --git a/img/email-preview-text.png b/img/email-preview-text.png new file mode 100644 index 0000000..d4548e4 Binary files /dev/null and b/img/email-preview-text.png differ diff --git a/img/gifs/star.gif b/img/gifs/star.gif new file mode 100644 index 0000000..1343e05 Binary files /dev/null and b/img/gifs/star.gif differ diff --git a/img/icons/bootstrap-email-logo.png b/img/icons/bootstrap-email-logo.png new file mode 100644 index 0000000..8ebf252 Binary files /dev/null and b/img/icons/bootstrap-email-logo.png differ diff --git a/img/icons/bootstrap-email-support.png b/img/icons/bootstrap-email-support.png new file mode 100644 index 0000000..81ea143 Binary files /dev/null and b/img/icons/bootstrap-email-support.png differ diff --git a/img/icons/check-green.svg b/img/icons/check-green.svg new file mode 100644 index 0000000..e4ecd4d --- /dev/null +++ b/img/icons/check-green.svg @@ -0,0 +1 @@ + diff --git a/img/icons/check.svg b/img/icons/check.svg new file mode 100644 index 0000000..d494aa4 --- /dev/null +++ b/img/icons/check.svg @@ -0,0 +1 @@ + diff --git a/img/icons/css.svg b/img/icons/css.svg new file mode 100644 index 0000000..bb5bd2e --- /dev/null +++ b/img/icons/css.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/icons/devices.svg b/img/icons/devices.svg new file mode 100644 index 0000000..ab18fce --- /dev/null +++ b/img/icons/devices.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/icons/logo-circle-small.png b/img/icons/logo-circle-small.png new file mode 100644 index 0000000..6a7d4e1 Binary files /dev/null and b/img/icons/logo-circle-small.png differ diff --git a/img/icons/logo-square.png b/img/icons/logo-square.png new file mode 100644 index 0000000..c396688 Binary files /dev/null and b/img/icons/logo-square.png differ diff --git a/img/icons/logo.png b/img/icons/logo.png new file mode 100644 index 0000000..14050fb Binary files /dev/null and b/img/icons/logo.png differ diff --git a/img/icons/x.svg b/img/icons/x.svg new file mode 100644 index 0000000..5df12b2 --- /dev/null +++ b/img/icons/x.svg @@ -0,0 +1 @@ + diff --git a/img/tree/1.jpeg b/img/tree/1.jpeg new file mode 100644 index 0000000..cd1856b Binary files /dev/null and b/img/tree/1.jpeg differ diff --git a/img/tree/2.jpeg b/img/tree/2.jpeg new file mode 100644 index 0000000..c766cc1 Binary files /dev/null and b/img/tree/2.jpeg differ diff --git a/img/tree/3.jpeg b/img/tree/3.jpeg new file mode 100644 index 0000000..5842b56 Binary files /dev/null and b/img/tree/3.jpeg differ diff --git a/img/tree/4.jpeg b/img/tree/4.jpeg new file mode 100644 index 0000000..9f2dcc8 Binary files /dev/null and b/img/tree/4.jpeg differ diff --git a/img/tree/5.jpeg b/img/tree/5.jpeg new file mode 100644 index 0000000..c7fd8f0 Binary files /dev/null and b/img/tree/5.jpeg differ diff --git a/img/tree/6.jpeg b/img/tree/6.jpeg new file mode 100644 index 0000000..aad757e Binary files /dev/null and b/img/tree/6.jpeg differ diff --git a/img/tree/7.jpeg b/img/tree/7.jpeg new file mode 100644 index 0000000..fd34c02 Binary files /dev/null and b/img/tree/7.jpeg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..dddc4e7 --- /dev/null +++ b/index.html @@ -0,0 +1,82 @@ +--- +layout: main +active: index +--- + + +
+
+
+
+
+

Cloud and HPC

+

Converged Computing encompasses the space between cloud and high performance computing (HPC), creating technologies and initiatives that encompass the best of both worlds. This portal aims to provide terminology, lessons, and learning materials to educate on the initiative.

+

Currently v0.1.0

+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+

Contributing Communities

+
    +
  • Lawrence Livermore National Laboratory
  • +
  • IBM Watson Research
  • +
  • Global Computing Lab
  • + +
+
+
+ +
+
+
+
+ +
+ +

Planned Content

+
+
+
+

Diagrams

+

Terms here will be supplemented with diagrams for further illustration.

+
+
+
+
+

Special Issues

+

Journal special issues or related content for converged computing will be updated here.

+
+
+
+
+

Projects

+

Specific and special projects oriented toward converged computing will be here.

+
+
+
+
+

Videos and Lessons

+

We will generate teaching material like videos for inclusion.

+
+
+
+
diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..3e79263 --- /dev/null +++ b/js/app.js @@ -0,0 +1,17 @@ +// Twitter BTN +window.twttr = (function(d, s, id) { + var js, fjs = d.getElementsByTagName(s)[0], + t = window.twttr || {}; + if (d.getElementById(id)) return t; + js = d.createElement(s); + js.id = id; + js.src = "https://platform.twitter.com/widgets.js"; + fjs.parentNode.insertBefore(js, fjs); + + t._e = []; + t.ready = function(f) { + t._e.push(f); + }; + + return t; +}(document, "script", "twitter-wjs")); diff --git a/js/cssdevices.js b/js/cssdevices.js new file mode 100644 index 0000000..14d8044 --- /dev/null +++ b/js/cssdevices.js @@ -0,0 +1,96 @@ +/*! CSSDevices v2.2.0 | MIT license | Maintained by Stuart Yamartino | http://cssdevices.io */ +$(function(){ + $('.cd-screen').each(function(){ + if($(this).hasClass('cd-smart-loader')){ + if(isSlideShow(this)){ + $('> :gt(0)', this).hide(); + } + $('> :first-child',this).each(function(){ + handleLoadBinding(this); + }); + } + else{ + callSlideShow(this); + } + }); + + function isSlideShow(_self){ + return $(_self).children().length > 1 && ! $(_self).hasClass('cd-no-slideshow'); + } + + function callSlideShow(_self){ + if(isSlideShow(_self)){ + var pauseSpeed = getOptionalData(_self, 'pause-speed', 5000); + var transitionSpeed = getOptionalData(_self, 'transition-speed', 1000); + $('> :gt(0)', _self).hide(); + if ( ! $(_self).hasClass('cd-smart-loader') ) { + $('> :eq(0)', _self).css('display', 'block'); + } + if( $(_self).hasClass('cd-transition-slider') ) { + setInterval(function(){$('> :first-child',_self).animate({ 'margin-left': '-100%' }, transitionSpeed).next().css({'display':'block','margin-left': '100%'}).animate({ 'margin-left': '0%' }, transitionSpeed).end().appendTo(_self);}, pauseSpeed); + } + else{ + setInterval(function(){$('> :first-child',_self).fadeOut(transitionSpeed).next().fadeIn(transitionSpeed).end().appendTo(_self);}, pauseSpeed); + } + } + } + + function handleLoadBinding(_self){ + $(_self).on('load', handleLoad); + if (_self.complete) { + $(_self).off('load', handleLoad); + handleLoad.call(_self); + } + } + + function handleLoad(){ + var loadSpeed = getOptionalData($(this).parent('.cd-smart-loader')[0], 'fade-in-speed', 250); + $(this).fadeIn(loadSpeed); + callSlideShow($(this).parent()[0]); + } + + function getOptionalData(saveThis, data, defaultValue){ + if ($(saveThis).attr('data-' + data)) { + return $(saveThis).data(data); + } + return defaultValue; + } + + $('.cd-device-loader').each(function(){ + if( ! $(this).hasClass('cd-fill-parent')){ + fadeDeviceIn(this); + } + }); + + //// Container Filler //// + var firstGo = true; + var scale; + fillContainer(); + $(window).resize(function(){ + fillContainer(); + }); + + function fillContainer(){ + $('.cd-fill-parent').each(function(){ + if( firstGo ){ + $(this).data('initial-width', $(this).width()); + } + if( $(this).hasClass('cd-padded-device')){ + var fontPercentage = ($(this).parent().width() - 40)/parseInt($(this).data('initial-width')) * 100; + } + else{ + var fontPercentage = $(this).parent().width()/parseInt($(this).data('initial-width')) * 100 - 10; + } + $(this).css('font-size', fontPercentage + '%'); + if( firstGo ){ + fadeDeviceIn(this); + } + }); + firstGo = false; + } + + function fadeDeviceIn(_self){ + $(_self).css('visibility', 'visible').hide().fadeIn(getOptionalData(_self, 'fade-in-speed', 250)); + } + +}); diff --git a/js/prism.js b/js/prism.js new file mode 100644 index 0000000..82c4c50 --- /dev/null +++ b/js/prism.js @@ -0,0 +1,6 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; +Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; diff --git a/pages/about.md b/pages/about.md new file mode 100644 index 0000000..e33fd8a --- /dev/null +++ b/pages/about.md @@ -0,0 +1,28 @@ +--- +layout: docs +title: "About" +permalink: /about +#sections: +# - What is Converged Computing +# - Why do we need to work together +--- + +Major trends have brought the cloud and high performance computing (HPC) communities closer together: the maturation of cloud technologies has shifted focus toward running workloads efficiently, and composite scientific workflows and resource heterogeneity and dynamism have revealed the limitations of traditional HPC resource and workflow management. It has become clear that the initially disparate communities have much to benefit in working together. Communication is a big part of that. What we've realized in our work is that when discussion happens between communities, the same terms might be used but have different meanings. For example, a "service" in Kubernetes means something [very specific](https://kubernetes.io/docs/concepts/services-networking/service/), while in HPC it might be generally describing a database or application programming interface. The term "scheduler" and what that encompasses also varies quite a bit between communities. + +This documentation site aims to bridge the gap between HPC and cloud to discuss terms for converged computing – the collaborative space between these traditionally separate communities – to develop novel technologies and applications. These hybrid technologies might span the gamut from automation, workflows, containerization, to software development and deployment and testing. This is a timely topic as collaborative work is happening to a greater degree that combines approaches from high performance computing with cloud-native computing. Being able to communicate effectively is essential. + +### Authors + +The original definitions were written by: + +- Vanessa Sochat, LLNL +- Daniel Milroy, LLNL +- Tapasya Patki, LLNL +- Claudia Misale (IBM Research) +- Jay Lofstead, Sandia National Laboratories +- Paula Olaya (University of Tennessee) +- Michela Taufer (University of Tennessee) +- Jakob Luettgau (previously University of Tennessee) + +And after this set, discussion moved into a Google Document that was open for community contribution, and discussion and work on the terms will continue on this site, with commit history much better representing contribution. I ([@vsoch](https://github.com/vsoch)) started the document and definitions realizing that the two communities were not communicating well, and hoping to work on that. If you'd like to further work on the terms or site content we welcome your contribution! +