Compare commits
No commits in common. "beb5368e283f1caa7d669473f20a3e1c2e6b9116" and "c5268ad9b3a87a92b8dbe4656bd96c477fe3ca46" have entirely different histories.
beb5368e28
...
c5268ad9b3
|
|
@ -1,56 +0,0 @@
|
|||
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
|
||||
# More GitHub Actions for Azure: https://github.com/Azure/actions
|
||||
|
||||
name: Build and deploy Node.js app to Azure Web App - bridgemanaccessible-ca
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node.js version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '18.x'
|
||||
|
||||
- name: npm install, build, and test
|
||||
run: |
|
||||
npm install
|
||||
npm run build --if-present
|
||||
npm run test --if-present
|
||||
|
||||
- name: Upload artifact for deployment job
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: node-app
|
||||
path: .
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
environment:
|
||||
name: 'Production'
|
||||
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
|
||||
|
||||
steps:
|
||||
- name: Download artifact from build job
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: node-app
|
||||
|
||||
- name: 'Deploy to Azure Web App'
|
||||
id: deploy-to-webapp
|
||||
uses: azure/webapps-deploy@v2
|
||||
with:
|
||||
app-name: 'bridgemanaccessible-ca'
|
||||
slot-name: 'Production'
|
||||
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_65BDB1C8D7104088A4D4720A2BB2F85A }}
|
||||
package: .
|
||||
7
.gitignore
vendored
|
|
@ -123,18 +123,11 @@ dist
|
|||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarnrc.yml
|
||||
.yarn/releases
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Private repository credentials etc...
|
||||
.npmrc
|
||||
|
||||
# Mac specific files
|
||||
.DS_Store
|
||||
|
||||
to-be-deleted
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
@BridgemanAccessible:registry=http://npm.pkg.bridgemanaccessible.ca/
|
||||
//npm.pkg.bridgemanaccessible.ca/:_authToken="<token>"
|
||||
29
Dockerfile
|
|
@ -1,29 +0,0 @@
|
|||
FROM node:latest
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package*.json ./
|
||||
COPY .npmrc ./
|
||||
COPY yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Build and test
|
||||
COPY tsconfig.json ./
|
||||
COPY ./src ./src
|
||||
COPY gulpfile.mjs ./
|
||||
RUN yarn build
|
||||
RUN npm run test --if-present
|
||||
|
||||
# Remove source files
|
||||
RUN rm -r ./src
|
||||
|
||||
ARG DOMAIN=example.com
|
||||
ENV DOMAIN=$DOMAIN
|
||||
|
||||
ARG PORT=3000
|
||||
ENV PORT=$PORT
|
||||
|
||||
EXPOSE $PORT
|
||||
CMD [ "yarn", "start" ]
|
||||
14
README.md
|
|
@ -1,12 +1,2 @@
|
|||
# Main Website - Bridgeman Accessible (bridgemanaccessible.ca)
|
||||
The Bridgeman Accessible main website
|
||||
|
||||
## Getting started
|
||||
Before you can start there is a little bit of required setup
|
||||
```sh
|
||||
# Create a `.npmrc` file (you cna copy-paste the `.npmrc.example` file provided replacing `<token>` with a proper value)
|
||||
mv ./.npmrc.example ./.npmrc
|
||||
|
||||
# If you need a token - login to the registry
|
||||
npm login --registry http://npm.pkg.bridgemanaccessible.ca/
|
||||
```
|
||||
# bridgemanaccessible.ca
|
||||
The Bridgeman Accessible website
|
||||
|
|
|
|||
102
gulpfile.mjs
|
|
@ -1,102 +0,0 @@
|
|||
import fse from 'fs-extra';
|
||||
import path from 'path';
|
||||
import gulp from "gulp";
|
||||
import ts from "gulp-typescript";
|
||||
//import * as dartSass from 'sass';
|
||||
//import gulpSass from 'gulp-sass';
|
||||
//import webpack from 'webpack';
|
||||
//import webpackStream from 'webpack-stream';
|
||||
import { deleteAsync } from 'del';
|
||||
|
||||
//const sass = gulpSass(dartSass);
|
||||
|
||||
var tsProject = ts.createProject("tsconfig.json");
|
||||
|
||||
// Task which would delete the old dist directory if present
|
||||
gulp.task("build-clean", function () {
|
||||
return deleteAsync(["./dist"]);
|
||||
});
|
||||
|
||||
// Task which would transpile typescript to javascript
|
||||
gulp.task("typescript", function () {
|
||||
return tsProject.src().pipe(tsProject()).js.pipe(gulp.dest("dist"));
|
||||
});
|
||||
|
||||
/*let webpackConfig = {
|
||||
mode: 'development', //(PRODUCTION ? 'production' : 'development'),
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [ "@babel/preset-env" ],
|
||||
compact: false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}//,
|
||||
//devtool: !PRODUCTION && 'source-map'
|
||||
}*/
|
||||
|
||||
// Task which will copy the assets from the static JavaScript directory to the dist directory
|
||||
/*gulp.task("compile-foundation-js", function () {
|
||||
return gulp.src('./src/static/js/foundation/** /*.js').pipe(webpackStream(webpackConfig, webpack)).pipe(gulp.dest("./dist/static/js/foundation"));
|
||||
});*/
|
||||
|
||||
// Task to compile the sass files to css files
|
||||
/*gulp.task("sass", function () {
|
||||
let includePaths = [];
|
||||
|
||||
if(fse.existsSync('node_modules/foundation-sites/scss') && fse.existsSync('node_modules/motion-ui/src')) {
|
||||
includePaths = [
|
||||
'node_modules/foundation-sites/scss',
|
||||
'node_modules/motion-ui/src'
|
||||
];
|
||||
}
|
||||
else if(fse.existsSync('.yarn/cache')) {
|
||||
// We're using Yarn PnP, so we need to find the paths to the foundation-sites and motion-ui packages
|
||||
const packages = fse.readdirSync('.yarn/cache');
|
||||
|
||||
const foundationSitesYarnPnPPath = path.join('.yarn/cache', packages.find(p => p.startsWith('foundation-sites-npm-') && p.endsWith('.zip')), 'node_modules/foundation-sites/scss');
|
||||
const motionUIYarnPnPPath = path.join('.yarn/cache', packages.find(p => p.startsWith('motion-ui-npm-') && p.endsWith('.zip')), 'node_modules/motion-ui/src');
|
||||
|
||||
includePaths = [
|
||||
foundationSitesYarnPnPPath,
|
||||
motionUIYarnPnPPath
|
||||
];
|
||||
}
|
||||
|
||||
return gulp.src("./src/static/scss/** /*.scss").pipe(sass({ includePaths: includePaths })).pipe(gulp.dest("./dist/static/css"));
|
||||
});*/
|
||||
|
||||
//gulp.task('pre-compile', gulp.parallel('compile-foundation-js', 'sass'));
|
||||
|
||||
// Task which would just create a copy of the current views directory in dist directory
|
||||
gulp.task("views", function () {
|
||||
return gulp.src("./src/pages/**/*.ejs").pipe(gulp.dest("./dist/pages"));
|
||||
});
|
||||
|
||||
// Task which will copy the assets from the static JavaScript directory to the dist directory
|
||||
gulp.task("assets-js", function () {
|
||||
return gulp.src(['./src/static/js/**/*.js', '!./src/static/js/foundation/**/*.js']).pipe(gulp.dest("./dist/static/js"));
|
||||
});
|
||||
|
||||
// Task which will copy the assets from the static image directory to the dist directory
|
||||
gulp.task("assets-img", function () {
|
||||
return gulp.src("./src/static/img/**/*", { encoding: false }).pipe(gulp.dest("./dist/static/img"));
|
||||
});
|
||||
|
||||
// Task which will copy the assets from the static CSS directory to the dist directory
|
||||
gulp.task("assets-css", function () {
|
||||
return gulp.src("./src/static/css/*").pipe(gulp.dest("./dist/static/css"));
|
||||
});
|
||||
|
||||
gulp.task("assets", gulp.parallel("assets-js", "assets-img", "assets-css"));
|
||||
|
||||
// The default task which runs at start of the gulpfile.js
|
||||
gulp.task("default", gulp.series("build-clean", "typescript", /*"pre-compile",*/ "views", "assets"), () => {
|
||||
console.log("Done");
|
||||
});
|
||||
20
package.json
|
|
@ -1,37 +1,23 @@
|
|||
{
|
||||
"name": "ba-website",
|
||||
"name": "my-express-app",
|
||||
"version": "1.0.0",
|
||||
"description": "A simple Express app",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node dist/server.js",
|
||||
"build": "gulp",
|
||||
"compile": "tsc && cp -R src/views dist/views",
|
||||
"dev": "nodemon src/server.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@BridgemanAccessible/ba-auth": "^1.0.0",
|
||||
"@BridgemanAccessible/ba-web-framework": "^1.0.0",
|
||||
"@BridgemanAccessible/listmonk-node-client": "^1.0.0",
|
||||
"ejs": "^3.1.6",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^11.3.0",
|
||||
"mime": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/gulp": "^4.0.17",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/node": "20.3.1",
|
||||
"del": "^8.0.0",
|
||||
"gulp": "^5.0.0",
|
||||
"gulp-sass": "^6.0.0",
|
||||
"gulp-typescript": "^6.0.0-alpha.1",
|
||||
"nodemon": "^2.0.13",
|
||||
"sass": "^1.85.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^5.4.2",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack-stream": "^7.0.0"
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="description" content="Bridgeman Accessible Website">
|
||||
<meta name="keywords" content="digital accessibility, accessible events, accessible websites, accessible documents, Bridgeman Accessible">
|
||||
<meta name="author" content="Bridgeman Accessible">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %> - Bridgeman Accessible</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/nav.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/accessibility.css">
|
||||
<% if (typeof extraStyles !== 'undefined') { %>
|
||||
<% if (Array.isArray(extraStyles)) { %>
|
||||
<% for (let style of extraStyles) { %>
|
||||
<link rel="stylesheet" type="text/css" href="/css/<%= style %>.css">
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<link rel="stylesheet" type="text/css" href="/css/<%= extraStyles %>.css">
|
||||
<% } %>
|
||||
<% } %>
|
||||
<!-- == Third Party Scripts == -->
|
||||
<!-- -- Analytics -- -->
|
||||
<!-- Google Tag Manager (YellowPages) -->
|
||||
<script>
|
||||
(
|
||||
function(w, d, s, l, i) {
|
||||
w[l] = w[l] || [];
|
||||
w[l].push({
|
||||
'gtm.start': new Date().getTime(),
|
||||
event: 'gtm.js'
|
||||
});
|
||||
var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : '';
|
||||
j.async = true;
|
||||
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
|
||||
f.parentNode.insertBefore(j, f);
|
||||
}
|
||||
)(window, document, 'script', 'dataLayer', 'GTM-MXV5B954');
|
||||
</script>
|
||||
<!-- End: Google Tag Manager -->
|
||||
<!-- Matomo (Us/Internal) -->
|
||||
<script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(
|
||||
function() {
|
||||
var u="//analytics.bridgemanaccessible.ca/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '1']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true;
|
||||
g.src=u+'matomo.js';
|
||||
s.parentNode.insertBefore(g,s);
|
||||
}
|
||||
)();
|
||||
</script>
|
||||
<!-- End: Matomo Code -->
|
||||
<!-- -- End: Analytics -- -->
|
||||
<script src="https://kit.fontawesome.com/e4af9e378a.js" crossorigin="anonymous"></script>
|
||||
<!-- == End: Third Party Scripts == -->
|
||||
<script type="application/javascript" src="js/hamburger.js" defer></script>
|
||||
<% if (typeof extraScripts !== 'undefined') { %>
|
||||
<% if (Array.isArray(extraScripts)) { %>
|
||||
<% for (let script of extraScripts) { %>
|
||||
<% if(typeof script === 'object') { %>
|
||||
<% if(script.script.startsWith('http') || script.script.startsWith('https')) { %>
|
||||
<script type="application/javascript" src="<%= script.script %>"></script>
|
||||
<% } else { %>
|
||||
<script type="application/javascript" src="/js/<%= script.script %>.js" <% if(script.defer) { %>defer<% } %>></script>
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<% if(script.startsWith('http') || script.startsWith('https')) { %>
|
||||
<script type="application/javascript" src="<%= script %>"></script>
|
||||
<% } else { %>
|
||||
<script type="application/javascript" src="/js/<%= script %>.js" defer></script>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<script type="application/javascript" src="/js/<%= extraScripts %>.js" defer></script>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="skip-link">Skip to content</a>
|
||||
<div class="content">
|
||||
<header>
|
||||
<% if(typeof header !== 'undefined') { %>
|
||||
<%- include(header) %>
|
||||
<% } else { %>
|
||||
<%- include('includes/header.ejs') %>
|
||||
<% } %>
|
||||
</header>
|
||||
<main role="main" id="main-content">
|
||||
<%- include(page) %>
|
||||
</main>
|
||||
<footer>
|
||||
<% if(typeof footer !== 'undefined') { %>
|
||||
<%- include(footer) %>
|
||||
<% } else { %>
|
||||
<%- include('includes/footer.ejs') %>
|
||||
<% } %>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<%- include('includes/header.ejs', {
|
||||
active_page: 'events'
|
||||
}) %>
|
||||
|
||||
<section class="events-page">
|
||||
<h1>Event IT Support by Bridgeman Accessible</h1>
|
||||
<p>
|
||||
For over five years, Bridgeman Accessible has been the trusted partner in providing top-notch IT support for a wide range of events, from intimate consultations to large-scale webinars with over 100 attendees.
|
||||
Whether your event requires single-camera setups or complex multi-camera arrangements, our expertise ensures seamless technology integration tailored to your needs.
|
||||
</p>
|
||||
<div>
|
||||
<h2>Proven Expertise and Experience</h2>
|
||||
<p>
|
||||
Our founder, equipped with a degree in computer science and a robust IT support background, began by volunteering tech support for events within the disability advocacy community.
|
||||
This dedication quickly earned us a reputation for excellence, with organizations like the Council of Canadians with Disabilities (CCD), the National Educational Association of Disabled Students (NEADS), and the Vision Impaired Resource Network (VIRN) consistently relying on our services.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Expanding Services to Personal Events</h2>
|
||||
<p>
|
||||
As our experience grew, so did the demand for our services.
|
||||
We have expanded beyond professional events to include personal occasions, such as weddings and special celebrations.
|
||||
Friends, family, and clients alike trust us to ensure their events run smoothly, whether online, hybrid, or in-person.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Cutting-Edge Technology at Your Service</h2>
|
||||
<p>
|
||||
Bridgeman Accessible leverages the latest technology to provide reliable event support.
|
||||
Our toolkit includes industry-leading solutions like Zoom, Microsoft Teams, Rode wireless microphones, the RODECaster Pro 2 sound production board, and the GoStream Deck Pro Kit, among others.
|
||||
We tailor our equipment and services to meet the unique requirements of your event, ensuring flawless execution every time.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%- include('includes/footer.ejs') %>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<a href="https://winnipeg-chamber.com/"><img src="img/Proud%20Chamber%20Web.webp" alt="Proud Winnipeg Chamber of Commerce member logo" /></a>
|
||||
<p>© 2023 Bridgeman Accessible. All rights reserved.</p>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<nav>
|
||||
<button class="hamburger-menu" aria-label="Menu" aria-expanded="false">
|
||||
<span class="hamburger-menu__line"></span>
|
||||
<span class="hamburger-menu__line"></span>
|
||||
<span class="hamburger-menu__line"></span>
|
||||
</button>
|
||||
<ul class="nav-links">
|
||||
<% if (title === 'Home') { %>
|
||||
<li><a href="/" class="nav-link active"><i class="fas fa-home"></i> Home</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="/" class="nav-link"><i class="fas fa-home"></i> Home</a></li>
|
||||
<% } %>
|
||||
<% if (title === 'Services') { %>
|
||||
<li><a href="services" class="nav-link active"><i class="fas fa-user-tie-hair"></i> Services</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="services" class="nav-link"><i class="fas fa-user-tie-hair"></i> Services</a></li>
|
||||
<% } %>
|
||||
<% if (title === 'Products') { %>
|
||||
<li><a href="products" class="nav-link active"><i class="fas fa-laptop-binary"></i> Products</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="products" class="nav-link"><i class="fas fa-laptop-binary"></i> Products</a></li>
|
||||
<% } %>
|
||||
<% if (title === 'About') { %>
|
||||
<li><a href="about" class="nav-link active"><i class="fas fa-info-circle"></i > About Us</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="about" class="nav-link"><i class="fas fa-info-circle"></i> About Us</a></li>
|
||||
<% } %>
|
||||
<li><a href="mailto:info@bridgemanaccessible.ca" class="nav-link"><i class="fas fa-envelope"></i> Contact Us</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
<section class="homepage-intro">
|
||||
<div class="homepage-banner__content">
|
||||
<h1 class="homepage-banner__title">Welcome to Bridgeman Accessible</h1>
|
||||
<p class="homepage-banner__text">Bridgeman Accessible is a small digital accessibility startup based in <%= business.location %> that specializes in digital and hybrid events and technology.</p>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item">
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/accessible%20event%20platform%20generic.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="side-by-side-item" style="display: flex;flex-direction: column;align-self: center">
|
||||
<h2>Creating accessible digital and hybrid events</h2>
|
||||
<p>
|
||||
Does this sound like you?
|
||||
<ul>
|
||||
<li>
|
||||
”With limited finances our event planners often find it hard to know what accessibility features to implement and it often ends up with inconsistent accessibility”
|
||||
</li>
|
||||
<li>
|
||||
”I always strive to make my events inclusive and accessible. But it's hard to remember everything”
|
||||
</li>
|
||||
<li>
|
||||
"I want to make my events accessible but I don't know where to start"
|
||||
</li>
|
||||
</p>
|
||||
<p>
|
||||
Overwhelmed by the thought of creating an accessible digital or hybrid event?
|
||||
We can help!
|
||||
We specialize in providing top-notch IT support for a wide range of events, from intimate consultations to larger-scale webinars with 100s of attendees.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item">
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/technology%20bridge.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="side-by-side-item" style="display: flex;flex-direction: column;align-self: center">
|
||||
<h2>Bridging digital accessibility into Tomorrow (Consultation and Collaboration)</h2>
|
||||
<p>
|
||||
Does this sound like you?
|
||||
<ul>
|
||||
<li>
|
||||
”Our team works hard at accessibility but their not experts and are often overwhelmed by the amount of information out there”
|
||||
</li>
|
||||
<li>
|
||||
”Accessibility for us is a moving target. We need help to keep up”
|
||||
</li>
|
||||
<li>
|
||||
"We know there are tools that can help us be more accessible but we don't know which ones are best for our needs"
|
||||
</li>
|
||||
</p>
|
||||
<p>
|
||||
We work closely with our clients on some of the most cutting edge and innovative technologies to help make sure their products and services have a clear path to become accessible for people living with disabilities.
|
||||
But not just a bridge good for today but tomorrow and into the future as well.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item">
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/devs%20working%20on%20innovation.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="side-by-side-item" style="display: flex;flex-direction: column;align-self: center">
|
||||
<h2>Innovation is what we do (Development)</h2>
|
||||
<p>
|
||||
Does this sound like you?
|
||||
<ul>
|
||||
<li>
|
||||
”Wish there was a tool out there that could help me as a person living with a disability get more done"
|
||||
</li>
|
||||
<li>
|
||||
”Training and individual wisdom is great but having a tool that could help us be more accessible would be even better”
|
||||
</li>
|
||||
<li>
|
||||
"We have a great idea for a tool that could help people living with disabilities but we don't know how to make it a reality"
|
||||
</li>
|
||||
</p>
|
||||
<p>
|
||||
We leverage the most modern technology, practices and techniques in combination with our own resourcefulness, ingenuity and creativity to create truly innovative solutions.
|
||||
These solutions aim to address the most pressing problems for modern professionals living with disabilities.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<img src="img/logo_with_black_background.png" alt="A white on black background version of the Bridgeman Accessible logo of a visually impaired person with a cane and someone in a wheelchair crossing a bridge which sits above a computer with a globe inside it with the words Bridgeman Accessible immediately below everything." />
|
||||
</section>
|
||||
|
|
@ -1,618 +0,0 @@
|
|||
<%- include('includes/header.ejs', {
|
||||
active_page: 'home'
|
||||
}) %>
|
||||
<h1>Infrastructure</h1>
|
||||
<section id="table-of-contents">
|
||||
<h2>Table of Contents</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#setup">Setup</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#bare-metal">Bare Metal</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#drives-and-raid">Drives and RAID</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#server-housing">Server Housing</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#network-infrastructure">Network Infrastructure</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#power">Power (UPS + Power Management Unit)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#monitoring-and-remote-control">Monitoring and Remote Control (IDRAC)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#operating-system">Operating System (Proxmox)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#kubernetes">Kubernetes</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#kubernetes-persistent-storage">Kubernetes Persistent Storage (Ceph/Rook)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#private-container-registry">Private Container Registry (Harbor)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#kubernetes-relational-database-cluster">Kubernetes Relational Database Cluster (CloudNativePG)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#private-npm-registry">Private NPM Registry (Verdaccio)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#cluster-and-service-monitoring">Cluster and Service Monitoring (Prometheus/Grafana)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#web-anayltics">Web Analytics (Matomo)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#cluster-ingress">Cluster Ingress (Nginx Ingress + MetalLB)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#local-network-dns">Local Network DNS (Bind)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#dhcp-and-filtering">DHCP and Filtering (PiHole)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#public-reverse-proxy">Public Reverse Proxy (Nginx Proxy Manager)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#public-service-monitoring">Public Service Monitoring (Uptime Kuma)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#dynamic-dns">Dynamic DNS (No-IP)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#public-dns">Public DNS (GoDaddy)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#email">Email (Mailcow + M365)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#alternate-port-smtp">Alternate Port SMTP (No-IP)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#newsletter-and-transactional-emails">Newsletter and Transactional Emails (Listmonk)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#social-media-management">Social Media Management (Mixpost)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#phone">Phone (FreePBX)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#groupware">Groupware (Nextcloud All-In-One)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#homepage">Internal Services Dashboard (Homepage)</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#costs">Costs</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#quick-links">Quick Links</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<hr>
|
||||
<section id="setup">
|
||||
<h2>Setup</h2>
|
||||
<div id="bare-metal">
|
||||
<h3>Bare Metal</h3>
|
||||
<p>
|
||||
We run almost all our workloads on our owned and operated "bare metal" servers.
|
||||
In particular, we bought 2 used servers:
|
||||
<ol>
|
||||
<li>Dell PowerEdge R730</li>
|
||||
<li>Dell PowerEdge R730xd</li>
|
||||
</ol>
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="drives-and-raid">
|
||||
<h3>Drives and RAID</h3>
|
||||
<p>
|
||||
The used Dell PowerEdge R730 we bought came with it's 8 drive bays full with 2 TB hard drives.
|
||||
We run this using RAID 5, which means that we have 14 TB of storage and can have a single drive fail without losing any data.
|
||||
</p>
|
||||
<p>
|
||||
The Dell PowerEdge R730xd we bought unfortunately came with no drives (not having experience with buying servers and the difference between "drives" and "drive bays").
|
||||
So, we bought 4 1TB Samsung SSDs to fill in 4 of it's 24 drive bays. Which we also learned we had to buy caddys for (and did).
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="server-housing">
|
||||
<h3>Server Housing</h3>
|
||||
<p>
|
||||
We house our servers on rails inside a small 12U Sysracks server cabinet in our office.
|
||||
This cabinet came with a built in fan, a lockable front door (lockable but removable side panels) and a power management unit.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="network-infrastructure">
|
||||
<h3>Network Infrastructure</h3>
|
||||
<p>
|
||||
We pay our ISP for a 1.5 Gbps connection, which is run through our ISP provided modem/router (which is in Bridge Mode).
|
||||
The ISP modem/router is then run to a TP-Link adapter (because we have a Powerline network connection to our office using another TP-Link adapter)
|
||||
We have to do this, this way because we lack the physical ability to run cabling through the physical structure.
|
||||
The TP-Link adapter in our office is then run to a Netgear Nighthawk router which creates vnets within our office.
|
||||
Which, for the purposes of this, is feed to a Netgear 1Gbps 8 port switch inside of our server cabinet.
|
||||
Which finally runs from the switch directly to our servers.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="power">
|
||||
<h3>Power (UPS + Power Management Unit)</h3>
|
||||
<p>
|
||||
We have a Tripplite 1500VA UPS that we use to power our servers for a short time in the event of a power outage.
|
||||
We have it plugged into the wall and then have our servers having one power supply plugged into it (the servers have two power supplies).
|
||||
We have the other power supply plugged into the power management unit in our server cabinet. Which the power management unit is plugged into the wall.
|
||||
We choose to set it up this way because we felt it was the architecture that best fit our risk profile.
|
||||
That is, we attempt to ensure that our servers would stay up (at least for a time) in the event of a power outage or UPS failure.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="monitoring-and-remote-control">
|
||||
<h3>Monitoring and Remote Control (IDRAC)</h3>
|
||||
<p>
|
||||
Amongst other reasons we choose to buy Dell servers because we wanted access via Integrated Dell Remote Access Controller (IDRAC).
|
||||
We use IDRAC to monitor and control our servers on a hardware level remotely.
|
||||
Though, we do have access to many of the same features/metrics through software having IDRAC provides us a piece of mind backup just in case.
|
||||
We have it set up so that we can ONLY access the IDRAC interface from our office network or VPN.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="operating-system">
|
||||
<h3>Operating System (Proxmox)</h3>
|
||||
<p>
|
||||
We run <a href="https://www.proxmox.com/proxmox-virtual-environment/overview">Proxmox Virtual Environment (PVE)</a> which is based on Debian Linux on both of our servers.
|
||||
This allows us to easily run and manage virtual workflows to maximize our hardware utilization.
|
||||
We also have them connected as a cluster within the PVE interface so that we can:
|
||||
<ul>
|
||||
<li>live migrate certain workloads between the two servers</li>
|
||||
<li>Have an active-backup setup for certain workloads</li>
|
||||
<li>Etc...</li>
|
||||
</ul>
|
||||
Though, it's worth noting this <strong>ISN'T</strong> a high availability cluster because you require at least a third server to do that.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kubernetes">
|
||||
<h3>Kubernetes</h3>
|
||||
<p>
|
||||
We run a <a href="https://kubernetes.io/">Kubernetes</a> cluster on top of our Proxmox cluster.
|
||||
We use this to run many (most) of our workloads.
|
||||
In particular, any workloads that can be containerized we try to (and/or aspire to) run on our Kubernetes cluster.
|
||||
</p>
|
||||
<p>
|
||||
At time of writing, we run one control node and four worker nodes.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kubernetes-persistent-storage">
|
||||
<h3>Kubernetes Persistent Storage (Ceph/Rook)</h3>
|
||||
<p>
|
||||
We run a <a href="https://ceph.io/">Ceph</a> cluster on top of Kubernetes using <a href="https://rook.io/">Rook</a>.
|
||||
We use this to store our persistent data.
|
||||
In particular, this includes storage for databases and other data that needs to be stored more permanently (across pod restarts/deployments).
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="private-container-registry">
|
||||
<h3>Private Container Registry (Harbor)</h3>
|
||||
<p>
|
||||
We run a private container registry on top of Kubernetes using <a href="https://goharbor.io/">Harbor</a>.
|
||||
We use this to store our container images.
|
||||
In particular, this includes images for our workloads that we deploy to our Kubernetes cluster.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kubernetes-relational-database-cluster">
|
||||
<h3>Kubernetes Relational Database Cluster (CloudNativePG)</h3>
|
||||
<p>
|
||||
We run a PostgreSQL cluster on top of Kubernetes using <a href="https://cloudnative-pg.io/">CloudNativePG</a>.
|
||||
We use this to store our relational data.
|
||||
In particular, this provides us another level of redundancy and reliability for our data.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="private-npm-registry">
|
||||
<h3>Private NPM Registry (Verdaccio)</h3>
|
||||
<p>
|
||||
We run a private NPM registry on top of Kubernetes using <a href="https://verdaccio.org/">Verdaccio</a>.
|
||||
We use this to store our NPM packages.
|
||||
In particular, this includes packages we tend to use with Node and other JavaScript based projects.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <a href="https://github.com/pypiserver/pypiserver">pypiserver</a> - Private PyPI registry? -->
|
||||
<div id="cluster-and-service-monitoring">
|
||||
<h3>Cluster and Service Monitoring (Prometheus/Grafana)</h3>
|
||||
<p>
|
||||
We run a <a href="https://prometheus.io/">Prometheus</a>/<a href="https://grafana.com/">Grafana</a> stack on top of Kubernetes.
|
||||
We use this to monitor our cluster and services.
|
||||
In particular, we use this to monitor the health of our cluster and services.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="web-anayltics">
|
||||
<h3>Web Analytics (Matomo)</h3>
|
||||
<p>
|
||||
We run a <a href="https://matomo.org/">Matomo</a> server on top of Kubernetes.
|
||||
We use this to track web traffic and usage statistics of our websites.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cluster-ingress">
|
||||
<h3>Cluster Ingress (Nginx Ingress + MetalLB)</h3>
|
||||
<p>
|
||||
We run an <a href="https://github.com/kubernetes/ingress-nginx">Nginx Ingress controller</a> alongside <a href="https://metallb.universe.tf/">MetalLB</a> on top of Kubernetes.
|
||||
We use this to route traffic to our services deployed on our Kubernetes cluster.
|
||||
The NGNIX Ingress controller is used to route traffic to our services and MetalLB is used, in our case, to obtain a network accessible IP address for the NGINX Ingress controller.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="local-network-dns">
|
||||
<h3>Local Network DNS (Bind)</h3>
|
||||
<p>
|
||||
We run two <a href="https://www.isc.org/bind/">Bind</a> DNS servers (a primary and secondary) outside our Kubernetes cluster.
|
||||
We use this to resolve DNS queries for our local corporate network.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dhcp-and-filtering">
|
||||
<h3>DHCP and Filtering (Pi-hole)</h3>
|
||||
<p>
|
||||
We run a <a href="https://pi-hole.net/">Pi-hole</a> server outside our Kubernetes cluster.
|
||||
We use this to provide DHCP and filter DNS queries for our local corporate network.
|
||||
It's worth noting that we generally prefer to use static DHCP leases over static IP addresses as it provides us more flexibility and easy oversight.
|
||||
That is, we can go to Pi-hole and see a single table with all the static leases rather than relying on a spreadsheet or other documentation to keep track of assigned static IPs
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="public-reverse-proxy">
|
||||
<h3>Public Reverse Proxy (Nginx Proxy Manager)</h3>
|
||||
<p>
|
||||
We run a <a href="https://nginxproxymanager.com/">Nginx Proxy Manager (NPM)</a> server outside of our Kubernetes cluster on a separate server/computer from the rest of our infrastructure.
|
||||
We use this to have more granular control of how (particularly public) traffic is routed within our network/to our services.
|
||||
Moreover, this usually handles TLS/SSL termination for our public services and getting and maintaining certificates through Let's Encrypt.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="public-service-monitoring">
|
||||
<h3>Public Service Monitoring (Uptime Kuma)</h3>
|
||||
<p>
|
||||
We run an <a href="https://uptime.kuma.pet/">Uptime Kuma</a> server outside of our Kubernetes cluster on a separate server/computer from the rest of our infrastructure.
|
||||
We use this to monitor the uptime of/access to our public services.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dynamic-dns">
|
||||
<h3>Dynamic DNS (No-IP)</h3>
|
||||
<p>
|
||||
We use <a href="https://www.noip.com/">No-IP</a> to provide us with a Dynamic DNS (DDNS) service for a small cost.
|
||||
That is, because our ISP wouldn't provide us a static IP address with the speed we wanted we use a DDNS service to have a name that resolves to our dynamic IP address.
|
||||
What this means practically, is we have a piece of software (provided by No-IP) running on one of our computers/servers that updates the No-IP record if our public IP address ever changes.
|
||||
We then use this in CNAME records for our public domain name so that our services can be publicly available without having to worry if our IP address ever changes.
|
||||
Though for the A records at the root of our domains we had to develop a script that did similar/the same as the No-IP software.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="public-dns">
|
||||
<h3>Public DNS (GoDaddy)</h3>
|
||||
<p>
|
||||
We use <a href="https://www.godaddy.com/">GoDaddy</a> to provide us with public DNS services for a cost.
|
||||
That is, we use GoDaddy to manage our public domain names and the DNS records associated with them.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="email">
|
||||
<h3>Email (Mailcow + M365)</h3>
|
||||
<p>
|
||||
We use a weird mix of services for email.
|
||||
Largely this is split between our self-hosted <a href="https://mailcow.email/">Mailcow</a> server and Microsoft 365.
|
||||
Our Mailcow server is used is used for internal email and access for our services.
|
||||
While Microsoft 365 is mostly used for external email.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="alternate-port-smtp">
|
||||
<h3>Alternate Port SMTP (No-IP)</h3>
|
||||
<p>
|
||||
We use <a href="https://www.noip.com/managed-mail#self-hosted">No-IP's alternate port SMTP service</a> for a small cost.
|
||||
That is, because our ISP blocks outbound traffic on port 25 we use a service that provides us with an alternate port to send email.
|
||||
We then configure this in our Mailcow server to send email out to the internet.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="newsletter-and-transactional-emails">
|
||||
<h3>Newsletter and Transactional Emails (Listmonk)</h3>
|
||||
<p>
|
||||
We use <a href="https://listmonk.app/">Listmonk</a> to send out our newsletters and transactional emails.
|
||||
Listmonk is a self-hosted solution that we host on our Kubernetes cluster.
|
||||
This connects with our Mailcow server to send out emails.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="social-media-management">
|
||||
<h3>Social Media Management (Mixpost)</h3>
|
||||
<p>
|
||||
We use <a href="https://mixpost.app/">Mixpost</a> to manage our social media accounts.
|
||||
Mixpost is a self-hosted solution that we host on our Kubernetes cluster.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="phone">
|
||||
<h3>Phone (FreePBX)</h3>
|
||||
<p>
|
||||
We use <a href="https://www.freepbx.org/">FreePBX</a> to provide us with phone services.
|
||||
This is a self-hosted solution that we host outside our Kubernetes cluster.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="groupware">
|
||||
<h3>Groupware (Nextcloud All-In-One)</h3>
|
||||
<p>
|
||||
We use <a href="https://github.com/nextcloud/all-in-one">Nextcloud All-In-One (AIO)</a> to provide us with groupware services.
|
||||
This is a self-hosted solution that we host outside our Kubernetes cluster (though aspirational would like to move onto Kubernetes).
|
||||
This provides us with file storage, calendar, contacts, and other groupware services.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="homepage">
|
||||
<h3>Internal Services Dashboard (Homepage)</h3>
|
||||
<p>
|
||||
We use <a href="https://gethomepage.dev/">Hmmepage</a> to provide a custom built dashboard of our services for internal users.
|
||||
This is a simple HTML page that we host on our Kubernetes cluster.
|
||||
It provides us with a quick links and an overview of the status of our services.
|
||||
</p>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr>
|
||||
<section id="costs">
|
||||
<h2>Costs</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Cost</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Dell PowerEdge R730</td>
|
||||
<td>$1,140.14</td>
|
||||
<td>Used server from Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dell PowerEdge R730xd</td>
|
||||
<td>$1,180.52</td>
|
||||
<td>Used server from Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dell Server Rack Rails (× 2)</td>
|
||||
<td>$288.96</td>
|
||||
<td>Used from Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12 pack 2.5'' Drive Caddy (× 2)</td>
|
||||
<td>$239.40</td>
|
||||
<td>From Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1TB Samsung SSDs (× 4)</td>
|
||||
<td>$582.36</td>
|
||||
<td>From Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12U Sysracks server cabinet</td>
|
||||
<td>$547.70</td>
|
||||
<td>From Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TP-Link adapter</td>
|
||||
<td>$291.18</td>
|
||||
<td>From Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Netgear Nighthawk router</td>
|
||||
<td>$146.44</td>
|
||||
<td>Used from individual</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Netgear 1Gbps 8 port switch</td>
|
||||
<td>$58.98</td>
|
||||
<td>Used from individual</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tripplite 1500VA UPS</td>
|
||||
<td>$517.87</td>
|
||||
<td>From Amazon.ca</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Power</td>
|
||||
<td>$597.00/year</td>
|
||||
<td>Approximate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Internet (from ISP)</td>
|
||||
<td>$1,464.96/month</td>
|
||||
<td>1.5Gbps speed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DDNS (from No-IP)</td>
|
||||
<td>$99.99/year</td>
|
||||
<td>Dynamic DNS service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Public DNS (from GoDaddy)</td>
|
||||
<td>$401.39/year</td>
|
||||
<td>DNS (for multiple domains) + M365</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Alternate Port SMTP (from No-IP)</td>
|
||||
<td>$39.99/year</td>
|
||||
<td>Alternate port SMTP service</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td>Fixed One Time/Capital</td>
|
||||
<td>$4,993.63</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Variable (Yearly Total)</td>
|
||||
<td>$2,603.33</td>
|
||||
<td>Or ~$216.95 per month</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</section>
|
||||
<hr>
|
||||
<section id="quick-links">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://www.proxmox.com/proxmox-virtual-environment/overview">Proxmox Virtual Environment (PVE)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://kubernetes.io/">Kubernetes</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://ceph.io/">Ceph</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://rook.io/">Rook</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://goharbor.io/">Harbor</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://cloudnative-pg.io/">CloudNativePG</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://verdaccio.org/">Verdaccio</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://prometheus.io/">Prometheus</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="grafana.com/">Grafana</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://matomo.org/">Matomo</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/kubernetes/ingress-nginx">Nginx Ingress controller</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://metallb.universe.tf/">MetalLB</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.isc.org/bind/">Bind</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://pi-hole.net/">Pi-hole</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://nginxproxymanager.com/">Nginx Proxy Manager (NPM)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://uptime.kuma.pet/">Uptime Kuma</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.noip.com/">No-IP</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.godaddy.com/">GoDaddy</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://mailcow.email/">Mailcow</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://listmonk.app/">Listmonk</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://mixpost.app/">Mixpost</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.freepbx.org/">FreePBX</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="gethomepage.dev/">Homepage</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<a href="#table-of-contents">Go to Table of Contents</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%- include('includes/footer.ejs') %>
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thermometer Layout</title>
|
||||
<link rel="stylesheet" href="/css/new-design-2.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<main class="content-area">
|
||||
<section class="card-section" aria-describedby="thermometer-indicator-section1">
|
||||
<h2 class="card-header">Section Title 1</h2>
|
||||
<div class="card-content">
|
||||
<p>This is the content for the first section. It can contain multiple paragraphs and other elements.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<button class="read-more">Read More</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-section" aria-describedby="thermometer-indicator-section2">
|
||||
<h2 class="card-header">Another Section</h2>
|
||||
<div class="card-content">
|
||||
<p>More interesting content for the second section goes here.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<button class="read-more">Learn More</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<aside class="thermometer-area" aria-label="Amount of managed by us spectrum">
|
||||
<div class="thermometer-graphic-container">
|
||||
<div class="thermometer-graphic" role="img" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="thermometer-bulb"></div>
|
||||
</div>
|
||||
<div class="thermometer-label">Amount of managed by us</div>
|
||||
</div>
|
||||
<div id="thermometer-indicator-section1" class="visually-hidden">Section 1 is located at approximately 30% in the managed amount spectrum.</div>
|
||||
<div id="thermometer-indicator-section2" class="visually-hidden">Section 2 is located at approximately 60% in the managed amount spectrum.</div>
|
||||
</aside>
|
||||
<div class="triangle-connector-container">
|
||||
<div class="triangle-connector top"></div>
|
||||
<div class="triangle-connector bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
<!-- <!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thermometer Layout</title>
|
||||
<link rel="stylesheet" href="/css/new-design-3.css">
|
||||
</head>
|
||||
<body> -->
|
||||
<div class="page-container">
|
||||
<div class="content-area">
|
||||
<section class="card-section top" aria-describedby="thermometer-indicator-section1">
|
||||
<div class="card-header">
|
||||
<h2>Self-Serve Tools (Products)</h2>
|
||||
<small>Price: Varies</small>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
We provide a variety of self-serve tools that can be used to help make technology (or people's use of technology) more accessible.
|
||||
These tools are designed to be user-friendly and can be used by individuals or organizations without the need for specialized training or expertise.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Accessibility Events Platform (AEP)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<a class="read-more" href="/products">Go to Products page</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-section middle" aria-describedby="thermometer-indicator-section2">
|
||||
<div class="card-header">
|
||||
<h2>Event, Media, Documents and Website Support</h2>
|
||||
<small>Price: $125/hour (subject to change)</small>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
We provide technical support for situations where there are disability/accessibility specific requirements or concerns.
|
||||
</p>
|
||||
<h3>Event Support</h3>
|
||||
<p>
|
||||
We specialize in helping facilitate any technical needs for accessibility features such as CART & captions, ASL/LSQ, and more.
|
||||
Our services are available for both online and hybrid events, and may be available for in-person events on a case by case basis.
|
||||
Noting, our office is located in Winnipeg, Manitoba, Canada and we are not able to travel outside of the city for in-person events at this time.
|
||||
</p>
|
||||
<!-- <p>
|
||||
More specifically, we often provide the following services (though may be open to others upon request):
|
||||
</p>
|
||||
<ul>
|
||||
<li>Event planning and coordination as it relates to needed technology and features (including researching and reporting on needed equipment, licenses or other resources or requirements)</li>
|
||||
<li>Event setup and troubleshooting</li>
|
||||
<li>Event moderation and technical problem troubleshooting</li>
|
||||
<li>Event recording and editing</li>
|
||||
</ul> -->
|
||||
<h3>Media Accessibility</h3>
|
||||
<p>We can help clients to remediate (make accessible) any media that they have created or are using.</p>
|
||||
<h3>Document Accessibility</h3>
|
||||
<p>We can help clients to remediate (make accessible) any documents that they have created or are using.</p>
|
||||
<p>
|
||||
Our past work has focused on PDFs, Word documents, PowerPoints and Excel spreadsheets.
|
||||
We are open to working with other document types as well, but with other formats we may not be able to provide the same level of support.
|
||||
</p>
|
||||
<h3>Website Advising/Testing</h3>
|
||||
<p>
|
||||
We do offer some limited website testing and advising services (that don't include development).
|
||||
Though this is somewhat limited (at least as A La Carte) compared to our many, many competitors in this area.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<a class="read-more" href="#">Read More</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-section middle" aria-describedby="thermometer-indicator-section3">
|
||||
<div class="card-header">
|
||||
<h2>Consulting and Co-Development</h2>
|
||||
<small>Price: $80/consultant or developer/hour (subject to change)</small>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
We work very closely with or alongside developers to make software and hardware accessible.
|
||||
Our team has extensive experience in not only accessibility (which many of our compeitors do as well) but in software development and engineering as well.
|
||||
Which means we provide you the uniquely specific experience and guidance that goes so much deeper than "you should do X because of Y which makes it more accessible" but the
|
||||
"your app is doing X which isn't accessible because of Y but if you look at line N in file M of your code you can do Z to fix it".
|
||||
</p>
|
||||
<p>
|
||||
This comes with the caveat that we are often more interested in the how than the what.
|
||||
Which means our clients are expected to have a good idea of what they want to build and what solutions may or may not work for them.
|
||||
We can help with this to an extent but we are not a design or content creation agency and we do not provide design or content development services.
|
||||
We have often had partners like disability specific organizations that can help with accessible design and content creation and we are happy to connect you and work with them as appropriate.
|
||||
</p>
|
||||
<p>
|
||||
In specific cases where the product or service will provide a meaningful and significant contribution to advancing the accessibility of technology we also offer co-development not only on accessibility but on the product or service in general.
|
||||
When applicable, we don't just bring accessibility into the development but also development expertise in areas such as infrastructure, security, testing and more.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Accessibility issue root cause analysis (and solution suggestion)</li>
|
||||
<li>Technial design as it relates to accessibility integration or subsystems</li>
|
||||
<li>Assistive technology or accessibility tool integration (ex. accessibility testing in CI pipelines suggestion and setup)</li>
|
||||
<li>Training and education (on developer tools, coding languages, APIs or other technical topics around accessibility)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<a class="read-more" href="mailto:info@bridgemanaccessible.ca">Contact Us</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-section bottom" aria-describedby="thermometer-indicator-section4">
|
||||
<div class="card-header">
|
||||
<h2>Disability Organization Technical Support</h2>
|
||||
<small>Price: $45/contractor/hour - minimum 10 hours (subject to change)</small>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
We provide IT support to disability-specific organizations or organiztions that work significanlty with people living with disabilities and help them become more digitally literate and efficient.
|
||||
Our team has experience working with a variety of organizations and can provide customized solutions to meet their unique needs.
|
||||
</p>
|
||||
<ul>
|
||||
<li>IT support and troubleshooting</li>
|
||||
<li>Training and education on technology use</li>
|
||||
<li>Assistive technology support</li>
|
||||
<li>Database management</li>
|
||||
<li>Website design and development</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<a class="read-more" href="mailto:info@bridgemanaccessible.ca">Contact Us</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<aside class="thermometer-area" aria-label="Amount of managed by us spectrum">
|
||||
<div class="thermometer-graphic-container">
|
||||
<div class="thermometer-graphic" role="img" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="thermometer-bulb"></div>
|
||||
</div>
|
||||
<div class="thermometer-label">Amount we're involved / managed by us</div>
|
||||
</div>
|
||||
<div id="thermometer-indicator-section1" class="visually-hidden">Located near the top at approximately the 25% in the managed amount spectrum.</div>
|
||||
<div id="thermometer-indicator-section2" class="visually-hidden">Located about half way down at approximately 50% in the managed amount spectrum.</div>
|
||||
<div id="thermometer-indicator-section3" class="visually-hidden">Located about three-quarters of the way down at approximately 75% in the managed amount spectrum.</div>
|
||||
<div id="thermometer-indicator-section4" class="visually-hidden">Located near the bottom at approximately 100% in the managed amount spectrum.</div>
|
||||
</aside>
|
||||
</div>
|
||||
<!-- </body>
|
||||
</html> -->
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thermometer Layout</title>
|
||||
<link rel="stylesheet" href="/css/new-design.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<main class="content-area">
|
||||
<section class="card-section" aria-describedby="thermometer-indicator-section1">
|
||||
<h2 class="card-header">Section Title 1</h2>
|
||||
<div class="card-content">
|
||||
<p>This is the content for the first section. It can contain multiple paragraphs and other elements.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<button class="read-more">Read More</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card-section" aria-describedby="thermometer-indicator-section2">
|
||||
<h2 class="card-header">Another Section</h2>
|
||||
<div class="card-content">
|
||||
<p>More interesting content for the second section goes here.</p>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<button class="read-more">Learn More</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<aside class="thermometer-area" aria-label="Amount of managed by us spectrum">
|
||||
<div class="thermometer-label">Amount of managed by us</div>
|
||||
<div class="thermometer-graphic" role="img" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
<div id="thermometer-indicator-section1" class="visually-hidden">Section 1 is located at approximately 30% in the managed amount spectrum.</div>
|
||||
<div id="thermometer-indicator-section2" class="visually-hidden">Section 2 is located at approximately 60% in the managed amount spectrum.</div>
|
||||
</aside>
|
||||
<div class="triangle-connector"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<p>
|
||||
Your data is stored on Bridgeman Accessible servers which reside in our office in Winnipeg, MB (Canada) and will only be used for distributing Bridgeman Accessible information unless given express permission.
|
||||
</p>
|
||||
<form id="newsletter-signup-form">
|
||||
<div>
|
||||
<label for="email">Email:</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="receive-sms" name="receive-sms">
|
||||
<label for="receive-sms">I would like to receive text (SMS) notifications about new newsletters and episodes</label>
|
||||
</div>
|
||||
<div id="phone-field" class="hidden">
|
||||
<label for="phone">Phone:</label>
|
||||
<input type="tel" id="phone" name="phone">
|
||||
</div>
|
||||
<%# <h2>Demographics</h2> %>
|
||||
<%# <p> %>
|
||||
<%# Because VI-Ed is a project of VIRN which is a registered Canadian charity we rely heavily on grants for our funding. %>
|
||||
<%# As part of many of these grants we are often asked to provide demographic information about our listeners. %>
|
||||
<%# So, we ask for your help by being open and honest about you information so that we can report honestly to our funders and continue this important work. %>
|
||||
<%# </p> %>
|
||||
<%# <div> %>
|
||||
<%# <input type="checkbox" id="minnedosa" name="minnedosa"> %>
|
||||
<%# <label for="minnedosa">I live in Minnedosa, Manitoba or surrounding area (as defined by the <a href="https://minnedosafoundation.com/">Minnedossa and District Community Foundation</a>)</label> %>
|
||||
<%# </div> %>
|
||||
<button type="submit">Sign Up</button>
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<h1>Our Products</h1>
|
||||
<section class="disclaimer">
|
||||
<p>
|
||||
It's important to note that products listed here may still be in development and not currently available for purchase. That said, if you are interested in one of these products we would encourage you to reach out to us and let us know because we may
|
||||
be able to give you early access to the product or have your feedback help shape the product.
|
||||
</p>
|
||||
<p>
|
||||
We will be trying to keep this page updated with the latest news as best we can so please keep checking back for updates!
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item">
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/accessible%20event%20platform%20generic.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/accessible%20event%20platform%20tablet.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/accessible%20event%20platform%20tablet%202.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="side-by-side-item" style="display: flex;flex-direction: column;align-self: center">
|
||||
<h2>Accessible Events Platform</h2>
|
||||
<small>Price (subscription + usage): $1,100/year or $100/month subscription + usage (subject to change)</small>
|
||||
<p>You can see the <a href="https://aep.bridgemanaccessible.ca">current prototype</a>. Though note, as a prototype features may or may not work and uptime/availability of the prototype is not guaranteed</p>
|
||||
<p>
|
||||
When your planning an event there are often a lot of things to think about.
|
||||
"Did I let them know the time?", "Do they have the link or know the room?", "Do I have all the speaker's materials?"
|
||||
And when someone asks you if you remembered to make sure it's accessible all you can think is, that that's one more thing to worry about.
|
||||
But this happens to most of us, we don't think about accessibility until someone asks us about it.
|
||||
But this isn't how it SHOULD be. Accessibility needs to be something we think about from the start and treated as a first class citizen.
|
||||
That's why we've built a purpose-built platform that will take that stress of one or a handful of people and put it on the platform (trust us, it can take it)
|
||||
</p>
|
||||
<p>
|
||||
But there is a lot to planning an accessible event and it can often be overwhelming and even more so if it's on top of the other planning responsibilities.
|
||||
So, often something gets missed or forgotten. Or even worse you've gotten feedback about the accessibility of the last event but you don't know what to do with it or you've forgotten and make the same mistake again.
|
||||
This is where our Accessible Events Platform comes in.
|
||||
</p>
|
||||
<p>
|
||||
It starts with a simple set of questions that walk you through one by one to get you thinking about accessibility and what you need to do to make your event accessible.
|
||||
But it doesn't stop there because we know that planning an event is a collaborative process and that you need to be able to share your plans with others and get feedback and input from them.
|
||||
So we've built in collaboration tools to help you do just that.
|
||||
And we've also built in tools to help you evaluate your event and get feedback from your attendees which shows up right at the start and tries to help you so that you don't have to worry about repeating the same mistakes over and over again.
|
||||
</p>
|
||||
<p>
|
||||
But no one is an expert in everything, which is why we went way passed just getting you to think about event accessibility and built a toolbox that you can pick and choose tools to use from to help make the event accessible including:
|
||||
</p>
|
||||
<ul>
|
||||
<li>An accessible website creator for your event website</li>
|
||||
<li>An accessible materials checker that can help make sure your promotions along with materials are accssible.</li>
|
||||
<li>A paid ask a member of the community feature</li>
|
||||
<li>A speaker preparation tool that can help your speakers prepare their presentations and materials to be accessible and will help you know where your at in collecting materials and making sure there is time to be accessible (that is send materials out ahead of time, or otherwise reschedule your event to allow for accessibility)</li>
|
||||
<li>Integration with our Event Support service (see the <a href="services">Our Services</a> page for details)</li>
|
||||
<li>A non-visual video editor that allows for anyone of any abilities to do video editing</li>
|
||||
<li>And much more!</li>
|
||||
</ul>
|
||||
<p>
|
||||
That's to say, our Accessible Events Platform is designed to be an all-in-one platform that helps people plan, execute, and evaluate accessible events of all sizes.
|
||||
Whether you're planning a meeting of two people or a conference of thousands, our platform will guide you through the process step by step and help you create and have an accessible event without putting even more stress on you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<h2>Accessible Questionnaire Platform</h2>
|
||||
<p>You can see the <a href="https://automatedbidsystem.blob.core.windows.net/prototype/index.html">current prototype</a>. Though note, as a prototype features may or may not work and uptime/availability of the prototype is not guaranteed</p>
|
||||
<p>
|
||||
Our Accessible Questionnaire Platform is designed to help you create accessible questionnaires and surveys.
|
||||
It's designed to be easy to use and learn and to be accessible to everyone regardless of their abilities.
|
||||
It's also designed to be flexible to various needs.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<h2>Non-Visual Video Editor (Codenamed VAP-E)</h2>
|
||||
<small>Price (subscription + usage): $150/year + $0.17/minute (subject to change)</small>
|
||||
<p>While a prototype isn't available at this time anyone interested is invited to read the <a href="https://github.com/AlanBridgeman/VAP-E_Extensions_Docs">Extensions document</a> that have been put up.</p>
|
||||
<p>In addition to being a part of the Accessible Events Platform we also offer the video editor as a standalone product.</p>
|
||||
<p>
|
||||
Our Non-Visual Video Editor is designed to allow anybody, regardless of their abilities, to edit videos.
|
||||
Where most video editor, put an emphasis on being able to see a video in order to edit it we take a more inclusive approach and allow for editing of videos without the need to see the video through mechanisms like audio cues, and transcript insertions.
|
||||
These cues help indicate where an action will happen and describe what that action is so that the user can make an informed decision about if that's the action they want to take at that point in the video.
|
||||
This allows us to make this a lot more accessible through mechanisms like forms and keyboard shortcuts.
|
||||
</p>
|
||||
<p>
|
||||
While we think this methodology will make it easy to use and learn with it's simple interface that is easy to navigate and understand.
|
||||
It's often the case that when editing you'll think something aligns with what you want but when you play it back it's not quite right.
|
||||
And while our mechansim can handle this it can become quite a prolonged process compared to other editors and this is why we've built in certain AI assisted features that can help you get the video to where you want it faster and more efficiently.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item">
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/screen%20magnifier%20img%201.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<img src="img/Bing-imgs/screen%20magnifier%20img%202.jpeg" alt="" />
|
||||
<figcaption>Generated by Bing using DALLE-3</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="side-by-side-item" style="display: flex;flex-direction: column;align-self: center">
|
||||
<h2>Multi-Tasking Screen Magnifier</h2>
|
||||
<small>Price (one time price): ~$500 (subject to change)</small>
|
||||
<p>As an idea you can watch the <a href="https://youtu.be/kswmpqTonys?si=TeeaAWk5aF9j2g10">prototype demo</a> we created</p>
|
||||
<p>Our Multi-Tasking Screen Magnifier is designed to help people with low vision or visual impairments use their computers more effectively. Our magnifier allows you to magnify specific windows or sections of your screen, rather than just the entire screen. You can also have multiple magnifiers open at once, making it easier to multitask. Some of the features of our magnifier include:</p>
|
||||
<ul>
|
||||
<li>Window-specific magnification</li>
|
||||
<li>Multiple magnifiers at once</li>
|
||||
<li>Easily learnable and consistant interface with other well known technologies (look similar to screen sharing in Zoom or Teams)</li>
|
||||
<li>Customizable magnification levels and colors</li>
|
||||
<li>Keyboard shortcuts for easy use</li>
|
||||
<li>And much more!</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
.skip-link {
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
top: -40px;
|
||||
left: 0;
|
||||
background-color: light-dark(#000,#fff);
|
||||
color: light-dark(#fff,#000);
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
padding: 10px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ nav {
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
background-color: light-dark(#fff, #000);
|
||||
box-shadow: 0 2px 4px light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2));
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
|
|
@ -15,12 +15,12 @@ nav {
|
|||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.hamburger-menu__line {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: light-dark(#333, #DDD);
|
||||
background-color: #333;
|
||||
margin: 5px 0;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ nav {
|
|||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: light-dark(#333, #DDD);
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
|
@ -71,15 +71,15 @@ nav {
|
|||
}
|
||||
|
||||
.nav-links li:last-child a {
|
||||
background-color: light-dark(#333, #DDD);
|
||||
color: light-dark(#fff, #000);
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 10px 15px;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
.nav-links li:last-child a:hover, .nav-links li:last-child a:focus {
|
||||
background-color: light-dark(#fff, #000);
|
||||
color: light-dark(#333, #DDD);
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
|
|
@ -1,22 +1,14 @@
|
|||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: light-dark(#fff, #000);
|
||||
color: light-dark(#000, #fff);
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
padding-bottom: 300px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
main {
|
||||
|
|
@ -26,7 +18,6 @@ main {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 2.6rem;
|
||||
}
|
||||
|
||||
section {
|
||||
|
|
@ -53,14 +44,14 @@ section {
|
|||
.homepage-banner__title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: light-dark(#000, #fff);
|
||||
color: #000;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.homepage-banner__text {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
color: light-dark(#000, #fff);
|
||||
color: #000;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +63,6 @@ section {
|
|||
|
||||
img {
|
||||
width: 100%;
|
||||
/*max-height: 30%;*/
|
||||
}
|
||||
|
||||
footer {
|
||||
|
|
@ -80,45 +70,12 @@ footer {
|
|||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
height: 100px;
|
||||
margin-top: auto;
|
||||
padding: 10px;
|
||||
background-color: light-dark(#333, #CCC);
|
||||
color: light-dark(#fff, #000);
|
||||
padding: 0;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.side-by-side {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.side-by-side:nth-child(2n) {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.side-by-side-item {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.side-by-side {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.side-by-side:nth-child(2n) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.side-by-side-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
0
src/static/img/alan.jpeg → src/public/img/alan.jpeg
Normal file → Executable file
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
|
@ -1,49 +0,0 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { Controller, GET, Page, BaseController } from '@BridgemanAccessible/ba-web-framework';
|
||||
|
||||
/**
|
||||
* The AboutController class is responsible for handling the about page of the site.
|
||||
*/
|
||||
@Controller()
|
||||
export class AboutController extends BaseController {
|
||||
/**
|
||||
* Route for rendering the site's about page.
|
||||
*
|
||||
* @param req The request object.
|
||||
* @param res The response object.
|
||||
*/
|
||||
@Page('About', 'about.ejs')
|
||||
@GET('/about')
|
||||
private about(req: Request, res: Response) {
|
||||
return {
|
||||
people: [
|
||||
{
|
||||
fname: 'Alan',
|
||||
lname: 'Bridgeman',
|
||||
position: 'Proprietor, CEO & Lead Developer',
|
||||
bio: '' +
|
||||
'Alan has been an avid disability advocate for over a decade including being a part of or involved with a variety of civic, provincial, national and international organizations around disability or accessibility. ' +
|
||||
'Alan has held numerous certifications including but not limited to: Mental Health First Aid, Certified Professional in Accessibility Core Competinies (CPACC) with the International Association of Accessibility Professionals (IAAP), among others. ' +
|
||||
'In additioan to his involvment in the disability community Alan is a dedicated and passionate software developer with a significant amount of experience in the industry. ' +
|
||||
'He has worked on a wide variety of projects, from small websites for research teams and non-profits to large enterprise applications at fourtune 500 companites like Microsoft. ' +
|
||||
'He has a strong background in web and cloud development, but has also worked on desktop and mobile applications. He has worked with many different technologies, including C#, Python, JavaScript, TypeScript, React, Node.js, Java, and many more. ' +
|
||||
'Alan started Bridgeman Accessible because he saw over and over again companies that were lead by non-disabled people and non-developers trying to solve digital accessibility problems. ' +
|
||||
'Moreover, they would only every consult or give advice but never build or innovate in ways that would help the community and the industry in a meaningful and impactful way.',
|
||||
image: 'alan.jpeg',
|
||||
website: 'https://alanbridgeman.ca/',
|
||||
email: 'alan@alanbridgeman.ca'
|
||||
},
|
||||
//{
|
||||
// fname: 'John',
|
||||
// lname: 'Oberton',
|
||||
// position: 'Infrastructure Developer (Project by Project basis/Contract)'
|
||||
//},
|
||||
//{
|
||||
// fname: 'Trevor',
|
||||
// lname: 'Xie',
|
||||
// position: 'Designer (Project by Project basis/Contract)'
|
||||
//}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { Controller, GET, Page, BaseController } from '@BridgemanAccessible/ba-web-framework';
|
||||
|
||||
/**
|
||||
* The HomeController class is responsible for handling the home page of the site.
|
||||
*/
|
||||
@Controller()
|
||||
export class HomeController extends BaseController {
|
||||
/**
|
||||
* Route for rendering the site's home page.
|
||||
*
|
||||
* @param req The request object.
|
||||
* @param res The response object.
|
||||
*/
|
||||
@Page('Home', 'index.ejs')
|
||||
@GET('/')
|
||||
private home(req: Request, res: Response) {
|
||||
return {
|
||||
business: {
|
||||
location: 'Winnipeg, Canada'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Page('New Design', 'new-design-3.ejs', [], ['new-design-3'])
|
||||
@GET('/new-design')
|
||||
private newPage(req: Request, res: Response) {}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import { Controller, GET, Page, POST, BaseController } from '@BridgemanAccessible/ba-web-framework';
|
||||
import { APICredentials, Subscriber, List } from '@BridgemanAccessible/listmonk-node-client';
|
||||
|
||||
@Controller()
|
||||
class NewsletterRoutes extends BaseController {
|
||||
/** Listmonk credentials */
|
||||
private creds: APICredentials;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Verify that the required environment variables are set
|
||||
if(typeof process.env.LISTMONK_HOST === 'undefined') { throw new Error('Missing environment variable: LISTMONK_HOST (hostname of Listmonk instance to use)'); }
|
||||
if(typeof process.env.LISTMONK_USERNAME === 'undefined') { throw new Error('Missing environment variable: LISTMONK_USERNAME (username to access Listmonk)'); }
|
||||
if(typeof process.env.LISTMONK_PASSWORD === 'undefined') { throw new Error('Missing environment variable: LISTMONK_PASSWORD (password to access Listmonk)'); }
|
||||
|
||||
this.creds = {
|
||||
host: process.env.LISTMONK_HOST,
|
||||
username: process.env.LISTMONK_USERNAME,
|
||||
password: process.env.LISTMONK_PASSWORD
|
||||
}
|
||||
}
|
||||
|
||||
@POST('/newsletter/signup', express.json())
|
||||
private async signUp(req: Request, res: Response) {
|
||||
const email = req.body.email;
|
||||
const name = req.body.name;
|
||||
|
||||
const attribs: { [key: string]: any } = {};
|
||||
if(typeof req.body.phone !== 'undefined') {
|
||||
attribs.phone = req.body.phone;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the newsletter list
|
||||
const list = await List.find((list: List) => list.getName() === 'Bridgeman Accessible Newsletter', this.creds);
|
||||
if(typeof list === 'undefined') {
|
||||
throw new Error('Newsletter list not found');
|
||||
}
|
||||
|
||||
// Create the subscriber
|
||||
await (await Subscriber.create(email, name, 'enabled', [list], attribs, false, this.creds)).save();
|
||||
}
|
||||
catch(e) {
|
||||
console.error(e);
|
||||
res.status(500).send('Issue with signing user up');
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Sign up successful' });
|
||||
}
|
||||
|
||||
@Page('Newsletter', 'newsletter.ejs', ['newsletter-signup'])
|
||||
@GET('/newsletter')
|
||||
private signupPage(req: Request, res: Response) {}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { Controller, GET, Page, BaseController } from '@BridgemanAccessible/ba-web-framework';
|
||||
|
||||
/**
|
||||
* The ProductsController class is responsible for handling the products page of the site.
|
||||
*/
|
||||
@Controller()
|
||||
export class ProductsController extends BaseController {
|
||||
/**
|
||||
* Route for rendering the site's products page.
|
||||
*
|
||||
* @param req The request object.
|
||||
* @param res The response object.
|
||||
*/
|
||||
@Page('Products', 'products.ejs')
|
||||
@GET('/products')
|
||||
private products(req: Request, res: Response) {}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { Controller, GET, Page, BaseController } from '@BridgemanAccessible/ba-web-framework';
|
||||
|
||||
/**
|
||||
* The ServicesController class is responsible for handling the services page of the site.
|
||||
*/
|
||||
@Controller()
|
||||
export class ServicesController extends BaseController {
|
||||
/**
|
||||
* Route for rendering the site's services page.
|
||||
*
|
||||
* @param req The request object.
|
||||
* @param res The response object.
|
||||
*/
|
||||
@Page('Services', 'services.ejs')
|
||||
@GET('/services')
|
||||
private services(req: Request, res: Response) {}
|
||||
}
|
||||
105
src/server.ts
|
|
@ -1,34 +1,81 @@
|
|||
import path from 'path';
|
||||
import { Application } from 'express';
|
||||
import { App, Initializer } from '@BridgemanAccessible/ba-web-framework';
|
||||
import { PointerServer } from '@BridgemanAccessible/ba-auth';
|
||||
import express from 'express';
|
||||
import mime from 'mime';
|
||||
|
||||
const domain = process.env.DOMAIN || 'localhost';
|
||||
const app = express();
|
||||
|
||||
/**
|
||||
* Callback for when the app starts.
|
||||
*
|
||||
* @param app The Express app that gets created/started.
|
||||
*/
|
||||
function onStart(app: Application) {
|
||||
// Currently within the Server class implementation,
|
||||
// which is then implemented by the accounts dashboard,
|
||||
// only the RFC8414 path/callback is setup by default
|
||||
//
|
||||
// this should be easy enough to change but don't want to fiddle with it right now
|
||||
//const oidcLink = PointerServer.createOIDCLink('account.bridgemanaccessible.ca');
|
||||
const rfc8414Link = PointerServer.createRFC8414Link('account.bridgemanaccessible.ca');
|
||||
PointerServer.setup(app, { links: [/*oidcLink, */rfc8414Link] });
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await (new App()).run(new Initializer({
|
||||
controllersPath: path.resolve(__dirname, 'routes'),
|
||||
staticFilesPath: path.resolve(__dirname, 'static'),
|
||||
view: {
|
||||
filesPath: path.resolve(__dirname, 'pages')
|
||||
app.get('/', (req, res) => {
|
||||
res.render('index.ejs', {
|
||||
title: 'Home',
|
||||
business: {
|
||||
location: 'Winnipeg, MB'
|
||||
}
|
||||
}), onStart);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
main();
|
||||
app.get('/about', (req, res) => {
|
||||
res.render('about.ejs', {
|
||||
title: 'About',
|
||||
people: [
|
||||
{
|
||||
fname: 'Alan',
|
||||
lname: 'Bridgeman',
|
||||
position: 'Proprietor, CEO & Lead Developer',
|
||||
bio: '' +
|
||||
'Alan has been an avid disability advocate for over a decade including being a part of or involved with a variety of civic, provincial, national and international organizations around disability or accessibility. ' +
|
||||
'Alan has held numerous certifications including but not limited to: Mental Health First Aid, Certified Professional in Accessibility Core Competinies (CPACC) with the International Association of Accessibility Professionals (IAAP), among others. ' +
|
||||
'In additioan to his involvment in the disability community Alan is a dedicated and passionate software developer with a significant amount of experience in the industry. ' +
|
||||
'He has worked on a wide variety of projects, from small websites for research teams and non-profits to large enterprise applications at fourtune 500 companites like Microsoft. ' +
|
||||
'He has a strong background in web and cloud development, but has also worked on desktop and mobile applications. He has worked with many different technologies, including C#, Python, JavaScript, TypeScript, React, Node.js, Java, and many more. ' +
|
||||
'Alan startecd Bridgeman Accessible because he saw over and over again companies that were lead by non-disabled people and non-developers trying to solve digital accessibility problems. ' +
|
||||
'Moreover, they would only every consult or give advice but never build or innovate in ways that would help the community and the industry in a meaningful and impactful way.',
|
||||
image: 'alan.jpeg',
|
||||
website: 'https://alanbridgeman.ca/',
|
||||
email: 'alan@alanbridgeman.ca'
|
||||
},
|
||||
//{
|
||||
// fname: 'John',
|
||||
// lname: 'Oberton',
|
||||
// position: 'Infrastructure Developer (Project by Project basis/Contract)'
|
||||
//},
|
||||
//{
|
||||
// fname: 'Trevor',
|
||||
// lname: 'Xie',
|
||||
// position: 'Designer (Project by Project basis/Contract)'
|
||||
//}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/services', (req, res) => {
|
||||
res.render('services.ejs', {
|
||||
title: 'Services'
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/products', (req, res) => {
|
||||
res.render('products.ejs', {
|
||||
title: 'Products'
|
||||
});
|
||||
});
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public'), {
|
||||
setHeaders: (res, path) => {
|
||||
if (mime.getType(path) === 'text/html') {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
}
|
||||
else if (mime.getType(path) === 'text/css') {
|
||||
res.setHeader('Content-Type', 'text/css');
|
||||
}
|
||||
else if (mime.getType(path) === 'application/javascript') {
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on port 3000');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
display: flex; /* Enable flexbox for overall layout */
|
||||
}
|
||||
|
||||
.page-container {
|
||||
display: flex; /* Enable flexbox for main content and thermometer areas */
|
||||
width: 100%;
|
||||
max-width: 1200px; /* Optional: Limit page width */
|
||||
margin: 20px auto; /* Center the container */
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
position: relative; /* For positioning the triangle connectors */
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 85%;
|
||||
padding-right: 5%; /* Spacing for the triangle */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-section {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
position: relative; /* To potentially position elements within the card */
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card-bottom .read-more {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.card-bottom .read-more:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
width: 10%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start; /* Align items to the top */
|
||||
padding-left: 5px; /* Spacing from the content area */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.thermometer-graphic-container {
|
||||
display: flex;
|
||||
flex-direction: row; /* Label to the right of the graphic */
|
||||
align-items: center; /* Vertically align label and graphic */
|
||||
margin-top: 20px; /* Adjust top margin as needed */
|
||||
}
|
||||
|
||||
.thermometer-graphic {
|
||||
background: linear-gradient(to bottom, #fdd835, #e1f5fe); /* Two-color fade/gradient */
|
||||
width: 30px; /* Adjust thickness as needed */
|
||||
height: 200px; /* Adjust height as needed */
|
||||
border-radius: 15px;
|
||||
border: 1px solid #ccc;
|
||||
position: relative; /* For positioning the bulb */
|
||||
}
|
||||
|
||||
.thermometer-bulb {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40px; /* Adjust bulb width */
|
||||
height: 40px; /* Adjust bulb height */
|
||||
background-color: #f44336; /* Thermometer bulb color */
|
||||
border-radius: 50%;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: -10px; /* Overlap with the main graphic */
|
||||
}
|
||||
|
||||
.thermometer-label {
|
||||
font-size: 0.8em;
|
||||
color: #777;
|
||||
margin-left: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.triangle-connector-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 85%; /* Align with the right edge of the content area */
|
||||
width: 5%; /* The gap between content and thermometer */
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around; /* Distribute triangles vertically */
|
||||
align-items: flex-end; /* Align triangles to the right */
|
||||
padding: 20px 0; /* Add some vertical padding */
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; /* Allow clicks to pass through */
|
||||
}
|
||||
|
||||
.triangle-connector {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 15px solid transparent; /* Adjust size */
|
||||
border-left: 15px solid white; /* Match card background */
|
||||
border-bottom: 15px solid transparent; /* Adjust size */
|
||||
}
|
||||
|
||||
/* Target the top triangle */
|
||||
.triangle-connector.top {
|
||||
border-top-width: 0; /* Start from a point */
|
||||
border-bottom-width: 20px; /* Adjust vertical expansion */
|
||||
border-left-width: 20px; /* Adjust horizontal expansion */
|
||||
border-left-color: white;
|
||||
}
|
||||
|
||||
/* Target the bottom triangle */
|
||||
.triangle-connector.bottom {
|
||||
border-top-width: 20px; /* Adjust vertical expansion */
|
||||
border-left-width: 20px; /* Adjust horizontal expansion */
|
||||
border-bottom-width: 0; /* End at a point */
|
||||
border-left-color: white;
|
||||
/* You might need to adjust positioning based on card placement */
|
||||
}
|
||||
|
||||
|
||||
/* Visually hidden class for accessibility */
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
margin: -1px !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* Basic responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
flex-direction: column; /* Stack elements on smaller screens */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
flex-direction: column;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
width: 80%;
|
||||
flex-direction: row; /* Label and graphic side by side */
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.thermometer-graphic-container {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.thermometer-label {
|
||||
writing-mode: horizontal-tb;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.triangle-connector-container {
|
||||
display: none; /* Hide triangles on smaller screens */
|
||||
}
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/*:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-size: 14pt;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: light-dark(#f4f4f4, #0C0C0C);
|
||||
color: light-dark(hsl(0, 0%, 0%), #ffffff);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex; /* Enable flexbox for overall layout /
|
||||
}*/
|
||||
|
||||
.page-container {
|
||||
display: flex; /* Enable flexbox for main content and thermometer areas */
|
||||
width: 100%;
|
||||
max-width: 1200px; /* Optional: Limit page width */
|
||||
margin: 1rem auto; /* Center the container */
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
position: relative; /* For positioning pseudo-elements absolutely */
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 85%;
|
||||
padding-right: 5%; /* Spacing for the triangle */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-section {
|
||||
background-color: light-dark(white, black);
|
||||
border: 1px solid light-dark(#ddd, #333);
|
||||
box-shadow: 0.2rem 0.2rem 0.7rem light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.2));
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px; /* Increased margin for visual separation */
|
||||
padding: 20px;
|
||||
position: relative; /* For positioning the ::after triangle */
|
||||
}
|
||||
|
||||
.card-section::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%; /* Vertically center the triangle */
|
||||
right: -3.65rem; /* Adjust to position it outside the card */
|
||||
transform: translateY(-50%);
|
||||
width: 0;
|
||||
height: 5%;
|
||||
border-top: 0 /*10px solid transparent*/; /* Adjust size */
|
||||
border-left: 4rem solid light-dark(white, black); /* Match card background */
|
||||
border-bottom: 11rem solid transparent; /* Adjust size */
|
||||
border-radius: 6px;
|
||||
z-index: 1; /* Ensure it's above other elements */
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
margin-top: 0;
|
||||
color: light-dark(#333, #DDD);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
color: light-dark(#555, #BBB);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card-bottom .read-more {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.card-bottom .read-more:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
width: 10%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start; /* Align items to the top */
|
||||
padding-left: 5px; /* Spacing from the content area */
|
||||
box-sizing: border-box;
|
||||
margin-left: 4rem;
|
||||
}
|
||||
|
||||
.thermometer-graphic-container {
|
||||
display: flex;
|
||||
flex-direction: row; /* Label to the right of the graphic */
|
||||
align-items: center; /* Vertically align label and graphic */
|
||||
margin-top: 20px; /* Adjust top margin as needed */
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.thermometer-graphic {
|
||||
background: linear-gradient(to bottom, light-dark(#e1f5fe, #1e0A01), #fdd835); /* Two-color fade/gradient */
|
||||
width: 2.5rem; /* Adjust thickness as needed */
|
||||
height: 102%; /* Adjust height as needed */
|
||||
border-radius: 15px;
|
||||
border: 1px solid light-dark(#ccc,#444);
|
||||
position: relative; /* For positioning the bulb */
|
||||
}
|
||||
|
||||
.thermometer-bulb {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 4rem; /* Match thermometer width */
|
||||
height: 4rem; /* Adjust bulb height */
|
||||
background: #fdd835; /* Match gradient */
|
||||
border-radius: 50%;
|
||||
border: none/*1px solid #ccc*/;
|
||||
margin-bottom: -5px; /* Slight overlap */
|
||||
}
|
||||
|
||||
.thermometer-label {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 14pt;
|
||||
color: light-dark(#777,#999);
|
||||
margin-left: 0.5rem;
|
||||
text-align: left;
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
|
||||
/* Visually hidden class for accessibility */
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
margin: -1px !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* Basic responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
/*body {
|
||||
flex-direction: column; /* Stack elements on smaller screens *
|
||||
align-items: center;
|
||||
}*/
|
||||
|
||||
.page-container {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card-bottom .read-more {
|
||||
display: block;
|
||||
width: 80%;
|
||||
height: auto;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-section::after {
|
||||
display: none; /* Hide triangles on smaller screens */
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
display: flex; /* Enable flexbox for overall layout */
|
||||
}
|
||||
|
||||
.page-container {
|
||||
display: flex; /* Enable flexbox for main content and thermometer areas */
|
||||
width: 100%;
|
||||
max-width: 1200px; /* Optional: Limit page width */
|
||||
margin: 20px auto; /* Center the container */
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 85%;
|
||||
padding-right: 5%; /* Spacing for the triangle */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-section {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.card-bottom .read-more {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.card-bottom .read-more:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
width: 10%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 5px; /* Spacing from the content area */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.thermometer-label {
|
||||
font-size: 0.8em;
|
||||
color: #777;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
writing-mode: vertical-lr; /* Make the label vertical */
|
||||
}
|
||||
|
||||
.thermometer-graphic {
|
||||
background: linear-gradient(to bottom, #fdd835, #e1f5fe); /* Two-color fade/gradient */
|
||||
width: 30px; /* Adjust thickness as needed */
|
||||
height: 200px; /* Adjust height as needed */
|
||||
border-radius: 15px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.triangle-connector {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 20px solid transparent; /* Adjust height to control triangle size */
|
||||
border-left: 20px solid white; /* Match card background */
|
||||
border-bottom: calc(100% - 20px) solid transparent; /* Adjust based on top border */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10%; /* Align with the thermometer area */
|
||||
box-sizing: border-box;
|
||||
pointer-events: none; /* Allow clicks to pass through */
|
||||
}
|
||||
|
||||
/* Visually hidden class for accessibility */
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
margin: -1px !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* Basic responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
flex-direction: column; /* Stack elements on smaller screens */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
flex-direction: column;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.thermometer-area {
|
||||
width: 80%;
|
||||
flex-direction: row; /* Label and graphic side by side */
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.thermometer-label {
|
||||
writing-mode: horizontal-tb;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.triangle-connector {
|
||||
display: none; /* Hide triangle on smaller screens */
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 332 KiB |
|
Before Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
|
@ -1,48 +0,0 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.getElementById('receive-sms').addEventListener('change', (e) => {
|
||||
document.getElementById('phone-field').classList.toggle('hidden');
|
||||
});
|
||||
|
||||
const form = document.getElementById('newsletter-signup-form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
// Prevent the form from submitting
|
||||
e.preventDefault();
|
||||
|
||||
// Get the form data
|
||||
const email = document.getElementById('email').value;
|
||||
const name = document.getElementById('name').value;
|
||||
|
||||
if(!email || !name) {
|
||||
alert('Please fill in all the required fields.');
|
||||
return;
|
||||
}
|
||||
|
||||
let phone = null;
|
||||
if(document.getElementById('receive-sms').checked) {
|
||||
phone = document.getElementById('phone').value;
|
||||
}
|
||||
|
||||
const data = { email: email, name: name, phone: phone };
|
||||
|
||||
// Send the data to the server
|
||||
fetch('/newsletter/signup', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
.then(response => {
|
||||
if(response.status !== 200) {
|
||||
alert('There was an error signing you up.')
|
||||
return;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
confirm(`${data.message}. Are you okay if we redirect you to the home page?`) ? window.location.href = '/' : null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
<%- include('header.ejs', {
|
||||
extra_css: [ 'about.css' ],
|
||||
active_page: 'about'
|
||||
}) %>
|
||||
|
||||
<h1>About Us</h1>
|
||||
|
||||
<section class="about-mission">
|
||||
|
|
@ -58,17 +63,13 @@
|
|||
(person) => {
|
||||
%>
|
||||
<li>
|
||||
<div class="side-by-side">
|
||||
<div class="side-by-side-item" style="padding: 1rem">
|
||||
<%
|
||||
if (typeof(person.image) !== 'undefined') {
|
||||
%>
|
||||
<img src="img/<%= person.image %>" alt="A photo of <%= person.fname %> <%= person.lname %>">
|
||||
<img src="img/<%= person.image %>" style="max-width:200px;height:auto;" alt="A photo of <%= person.fname %> <%= person.lname %>">
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
<div class="side-by-side-item">
|
||||
<h3><%= person.fname %> <%= person.lname %></h3>
|
||||
<p><%= person.position %></p>
|
||||
<%
|
||||
|
|
@ -92,12 +93,12 @@
|
|||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<%
|
||||
}
|
||||
);
|
||||
%>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<%- include('footer.ejs') %>
|
||||
7
src/views/footer.ejs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
</main>
|
||||
<footer>
|
||||
<p>© 2023 Bridgeman Accessible. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
59
src/views/header.ejs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %> - Bridgeman Accessible</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/nav.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/accessibility.css">
|
||||
<%
|
||||
if(typeof(extra_css) !== 'undefined') {
|
||||
extra_css.forEach(
|
||||
(css) => {
|
||||
%>
|
||||
<link rel="stylesheet" type="text/css" href="css/<%= css %>">
|
||||
<%
|
||||
}
|
||||
);
|
||||
}
|
||||
%>
|
||||
<script src="https://kit.fontawesome.com/e4af9e378a.js" crossorigin="anonymous"></script>
|
||||
<script type="application/javascript" src="js/hamburger.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="skip-link">Skip to content</a>
|
||||
<div class="content">
|
||||
<header>
|
||||
<nav>
|
||||
<button class="hamburger-menu" aria-label="Menu" aria-expanded="false">
|
||||
<span class="hamburger-menu__line"></span>
|
||||
<span class="hamburger-menu__line"></span>
|
||||
<span class="hamburger-menu__line"></span>
|
||||
</button>
|
||||
<ul class="nav-links">
|
||||
<% if (active_page === 'home') { %>
|
||||
<li><a href="/" class="nav-link active"><i class="fas fa-home"></i> Home</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="/" class="nav-link"><i class="fas fa-home"></i> Home</a></li>
|
||||
<% } %>
|
||||
<% if (active_page === 'services') { %>
|
||||
<li><a href="services" class="nav-link active"><i class="fas fa-user-tie-hair"></i> Services</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="services" class="nav-link"><i class="fas fa-user-tie-hair"></i> Services</a></li>
|
||||
<% } %>
|
||||
<% if (active_page === 'products') { %>
|
||||
<li><a href="products" class="nav-link active"><i class="fas fa-laptop-binary"></i> Products</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="products" class="nav-link"><i class="fas fa-laptop-binary"></i> Products</a></li>
|
||||
<% } %>
|
||||
<% if (active_page === 'about') { %>
|
||||
<li><a href="about" class="nav-link active"><i class="fas fa-info-circle"></i > About Us</a></li>
|
||||
<% } else { %>
|
||||
<li><a href="about" class="nav-link"><i class="fas fa-info-circle"></i> About Us</a></li>
|
||||
<% } %>
|
||||
<li><a href="mailto:info@bridgemanaccessible.ca" class="nav-link"><i class="fas fa-envelope"></i> Contact Us</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<main role="main" id="main-content">
|
||||
13
src/views/index.ejs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<%- include('header.ejs', {
|
||||
active_page: 'home'
|
||||
}) %>
|
||||
|
||||
<section class="homepage-intro">
|
||||
<div class="homepage-banner__content">
|
||||
<h1 class="homepage-banner__title">Welcome to Bridgeman Accessible</h1>
|
||||
<p class="homepage-banner__text">Bridgeman Accessible is a small digital accessibility startup based in <%= business.location %></p>
|
||||
</div>
|
||||
<img src="img/logo_with_black_background.png" alt="A white on black background version of the Bridgeman Accessible logo of a visually impaired person with a cane and someone in a wheelchair crossing a bridge which sits above a computer with a globe inside it with the words Bridgeman Accessible immediately below everything." />
|
||||
</section>
|
||||
|
||||
<%- include('footer.ejs') %>
|
||||
87
src/views/products.ejs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<%- include('header.ejs', {
|
||||
active_page: 'products'
|
||||
}) %>
|
||||
|
||||
<h1>Our Products</h1>
|
||||
<section class="disclaimer">
|
||||
<p>
|
||||
It's important to note that products listed here may still be in development and not currently available for purchase. That said, if you are interested in one of these products we would encourage you to reach out to us and let us know because we may
|
||||
be able to give you early access to the product or have your feedback help shape the product.
|
||||
</p>
|
||||
<p>
|
||||
We will be trying to keep this page updated with the latest news as best we can so please keep checking back for updates!
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<h2>Accessible Events Platform</h2>
|
||||
<small>Price (subscription + usage): $1,100/year or $100/month subscription + usage (subject to change)</small>
|
||||
<p>
|
||||
When your planning an event there are often a lot of things to think about.
|
||||
"Did I let them know the time?", "Do they have the link or know the room?", "Do I have all the speaker's materials?"
|
||||
And when someone asks you if you remembered to make sure it's accessible all you can think is, that that's one more thing to worry about.
|
||||
But this happens to most of us, we don't think about accessibility until someone asks us about it.
|
||||
But this isn't how it SHOULD be. Accessibility needs to be something we think about from the start and treated as a first class citizen.
|
||||
That's why we've built a purpose-built platform that will take that stress of one or a handful of people and put it on the platform (trust us, it can take it)
|
||||
</p>
|
||||
<p>
|
||||
But there is a lot to planning an accessible event and it can often be overwhelming and even more so if it's on top of the other planning responsibilities.
|
||||
So, often something gets missed or forgotten. Or even worse you've gotten feedback about the accessibility of the last event but you don't know what to do with it or you've forgotten and make the same mistake again.
|
||||
This is where our Accessible Events Platform comes in.
|
||||
</p>
|
||||
<p>
|
||||
It starts with a simple set of questions that walk you through one by one to get you thinking about accessibility and what you need to do to make your event accessible.
|
||||
But it doesn't stop there because we know that planning an event is a collaborative process and that you need to be able to share your plans with others and get feedback and input from them.
|
||||
So we've built in collaboration tools to help you do just that.
|
||||
And we've also built in tools to help you evaluate your event and get feedback from your attendees which shows up right at the start and tries to help you so that you don't have to worry about repeating the same mistakes over and over again.
|
||||
</p>
|
||||
<p>
|
||||
But no one is an expert in everything, which is why we went way passed just getting you to think about event accessibility and built a toolbox that you can pick and choose tools to use from to help make the event accessible including:
|
||||
</p>
|
||||
<ul>
|
||||
<li>An accessible website creator for your event website</li>
|
||||
<li>An accessible materials checker that can help make sure your promotions along with materials are accssible.</li>
|
||||
<li>A paid ask a member of the community feature</li>
|
||||
<li>A speaker preparation tool that can help your speakers prepare their presentations and materials to be accessible and will help you know where your at in collecting materials and making sure there is time to be accessible (that is send materials out ahead of time, or otherwise reschedule your event to allow for accessibility)</li>
|
||||
<li>Integration with our Event Support service (see the <a href="services">Our Services</a> page for details)</li>
|
||||
<li>A non-visual video editor that allows for anyone of any abilities to do video edititing</li>
|
||||
<li>And much more!</li>
|
||||
</ul>
|
||||
<p>
|
||||
That's to say, our Accessible Events Platform is designed to be an all-in-one platform that helps people plan, execute, and evaluate accessible events of all sizes.
|
||||
Whether you're planning a meeting of two people or a conference of thousands, our platform will guide you through the process step by step and help you create and have an accessible event without putting even more stress on you.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<h2>Non-Visual Video Editor (Codenamed VAP-E)</h2>
|
||||
<small>Price (subscription + usage): $150/year + $0.17/minute (subject to change)</small>
|
||||
<p>In addition to being a part of the Accessible Events Platform we also offer the video editor as a standalone product.</p>
|
||||
<p>
|
||||
Our Non-Visual Video Editor is designed to allow anybody, regardless of their abilities, to edit videos.
|
||||
Where most video editor, put an emphasis on being able to see a video in order to edit it we take a more inclusive approach and allow for editing of videos without the need to see the video through mechanisms like audio cues, and transcript insertions.
|
||||
These cues help indicate where an action will happen and describe what that action is so that the user can make an informed decision about if that's the action they want to take at that point in the video.
|
||||
This allows us to make this a lot more accessible through mechanisms like forms and keyboard shortcuts.
|
||||
</p>
|
||||
<p>
|
||||
While we think this methodology will make it easy to use and learn with it's simple interface that is easy to navigate and understand.
|
||||
It's often the case that when editing you'll think something aligns with what you want but when you play it back it's not quite right.
|
||||
And while our mechansim can handle this it can become quite a prolonged process compared to other editors and this is why we've built in certain AI assisted features that can help you get the video to where you want it faster and more efficiently.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="product">
|
||||
<h2>Multi-Tasking Screen Magnifier</h2>
|
||||
<small>Price (one time price): ~$500 (subject to change)</small>
|
||||
<p>Our Multi-Tasking Screen Magnifier is designed to help people with low vision or visual impairments use their computers more effectively. Our magnifier allows you to magnify specific windows or sections of your screen, rather than just the entire screen. You can also have multiple magnifiers open at once, making it easier to multitask. Some of the features of our magnifier include:</p>
|
||||
<ul>
|
||||
<li>Window-specific magnification</li>
|
||||
<li>Multiple magnifiers at once</li>
|
||||
<li>Easily learnable and consistant interface with other well known technologies (look similar to screen sharing in Zoom or Teams)</li>
|
||||
<li>Customizable magnification levels and colors</li>
|
||||
<li>Keyboard shortcuts for easy use</li>
|
||||
<li>And much more!</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<%- include('footer.ejs') %>
|
||||
|
|
@ -1,30 +1,27 @@
|
|||
<%- include('header.ejs', {
|
||||
active_page: 'services'
|
||||
}) %>
|
||||
|
||||
<h1>Our Services</h1>
|
||||
|
||||
<section class="service">
|
||||
<h2>Event and Assistive Technology Support</h2>
|
||||
<h2>Event Support</h2>
|
||||
<small>Price: $125/hour (subject to change)</small>
|
||||
<p>
|
||||
We provide technical support for situations where there are disability/accessibility specific requirements or concerns.
|
||||
</p>
|
||||
<h3>Event Support</h3>
|
||||
<p>
|
||||
We specialize in features such as captions, ASL/LSQ, English/French interpretation, and more.
|
||||
Our services are available for both online and hybrid events, and may be available for in-person events on a case by case basis.
|
||||
We provide technical support for events, particularly if they require accessibility features such as captions, ASL/LSQ, English/French interpretation, and more.
|
||||
Our services are available for both online and hybrid events, and may be availabe for in-person events on a case by case basis.
|
||||
</p>
|
||||
<p>
|
||||
More specifically, we often provide the following services (though may be open to others upon request):
|
||||
</p>
|
||||
<ul>
|
||||
<li>Event planning and coordination as it relates to needed technology and features (including researching and reporting on needed equipment, licenses or other resources or requirements)</li>
|
||||
<li>Event planning and coordination as it relates to needed technology and features (inluding researching and reporting on needed equipment, licenses or other resources or requirements)</li>
|
||||
<li>Event setup and troubleshooting</li>
|
||||
<li>Event moderation and technical problem troubleshooting</li>
|
||||
<li>Event recording and editing</li>
|
||||
</ul>
|
||||
<h3>Specialized Technology Support</h3>
|
||||
<p>
|
||||
We allow clients to feel confident that their employees of any abilities will be well supported when it comes to technology.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="service">
|
||||
<h2>Co-Development and Consulting</h2>
|
||||
<small>Price: $80/hour (subject to change)</small>
|
||||
|
|
@ -50,6 +47,7 @@
|
|||
<li>Training and education (on developer tools, coding languages, APIs or other technical topics around accessibility)</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="service">
|
||||
<h2>Disability Organization Technical Support</h2>
|
||||
<small>Price: $45/hour (subject to change)</small>
|
||||
|
|
@ -64,4 +62,6 @@
|
|||
<li>Database management</li>
|
||||
<li>Website design and development (limited)</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<%- include('footer.ejs') %>
|
||||
112
tsconfig.json
|
|
@ -1,108 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": ["DOM", "ES2022", "ESNext.AsyncIterable"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": ".", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"module": "commonjs",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": false,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": false,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"exclude": [ "node_modules" ],
|
||||
"include": [
|
||||
"./src/**/*.tsx",
|
||||
"./src/**/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||